|
@@ -1,171 +1,162 @@
|
|
|
-<!-- https://www.createwithdata.com/chartjs-and-csv/ -->
|
|
|
-<!-- https://towardsdatascience.com/4-ways-to-improve-your-plotly-graphs-517c75947f7e -->
|
|
|
-
|
|
|
<?php
|
|
|
-$branch = 'dev';
|
|
|
-
|
|
|
-function getsmth($branch){
|
|
|
- $url = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/";
|
|
|
- $text = file_get_contents($url);
|
|
|
- $arrays = explode("\n", $text);
|
|
|
- $clean_arrs = array_filter($arrays, function($value){
|
|
|
- $temp_arr = explode('/', $value);
|
|
|
- return preg_match("/[A-Z]+[0-9]+\//", $value);
|
|
|
- });
|
|
|
- foreach($clean_arrs as &$val){
|
|
|
- $val = substr($val, 0, -1);
|
|
|
- }
|
|
|
- return $clean_arrs;
|
|
|
-}
|
|
|
-
|
|
|
-$availableSeasons = getsmth($branch);
|
|
|
-
|
|
|
-$season = isset($_GET["season"])&&in_array($_GET["season"], $availableSeasons) ? $_GET["season"] : reset($availableSeasons);
|
|
|
-$url = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/".$season."/";
|
|
|
-$url_total_info = "https://cmd.inp.nsk.su/~compton/gitlist/compton_tables/raw/".$branch."/tables/".$season.".csv";
|
|
|
-$text = file_get_contents($url);
|
|
|
-$arrays = explode("\n", $text);
|
|
|
-
|
|
|
-$cleanArrs = array_filter($arrays, function($value) {
|
|
|
- return end(explode('.', $value)) == "csv";
|
|
|
-});
|
|
|
-
|
|
|
-function isSelected($a, $b){
|
|
|
- if ($a==$b){
|
|
|
- return "selected";
|
|
|
- }
|
|
|
- return "";
|
|
|
-}
|
|
|
-
|
|
|
-$selected_csv = isset($_GET["csv_file"])&&in_array($_GET["csv_file"], $cleanArrs) ? $_GET["csv_file"] : reset($cleanArrs);
|
|
|
+require __DIR__ . '/commonfoos.php';
|
|
|
+
|
|
|
+$ini_array = parse_ini_file("config.ini");
|
|
|
+$token = $ini_array["apitoken"];
|
|
|
+
|
|
|
+$availableSeasons = available_seasons($host, $owner, $repo, "energy_points", $token);
|
|
|
+
|
|
|
+$season = isset($_GET["season"]) && in_array($_GET["season"], $availableSeasons) ? $_GET["season"] : end($availableSeasons);
|
|
|
+$season_url = season_table_url($host, $owner, $repo, "", $token, $season);
|
|
|
+
|
|
|
+$energyPoints = energy_point_urls($host, $owner, $repo, "energy_points/{$season}", $token);
|
|
|
|
|
|
+$selected_csv = isset($_GET["csv_file"]) && array_key_exists($_GET["csv_file"], $energyPoints) ? $_GET["csv_file"] : key($energyPoints);
|
|
|
?>
|
|
|
|
|
|
<html>
|
|
|
+
|
|
|
<head>
|
|
|
<script src="plotly-latest.min.js"></script>
|
|
|
<!-- <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> -->
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
<link rel="stylesheet" href="main.css">
|
|
|
<title>Compton interactive plots</title>
|
|
|
- <!-- <script src="chart.js"></script> -->
|
|
|
</head>
|
|
|
+
|
|
|
<body>
|
|
|
<div id="gd" style="height: max(80vh, 600px); padding-bottom: 20px;"></div>
|
|
|
-
|
|
|
+
|
|
|
<form name="form" action="" method="get">
|
|
|
<p style="text-align: center;">Select energy point:</p>
|
|
|
- <div style="margin: 0 auto; display: flex; justify-content: center;">
|
|
|
- <select name="season" class="select-css" style="margin: 0;" onchange="this.form.submit()">
|
|
|
- <? foreach($availableSeasons as $s){ ?>
|
|
|
- <option value="<?echo $s?>" <?echo isSelected($s, $season)?>><?echo $s?></option>
|
|
|
- <? } ?>
|
|
|
- </select>
|
|
|
- <select name="csv_file" class="select-css" style="margin: 0;" onchange="this.form.submit()">
|
|
|
- <? foreach($cleanArrs as $file){ ?>
|
|
|
- <option value="<?echo $file?>" <?echo isSelected($file, $selected_csv)?>><?echo $file?></option>
|
|
|
- <? } ?>
|
|
|
- </select>
|
|
|
- </div>
|
|
|
+ <div style="margin: 0 auto; display: flex; justify-content: center;">
|
|
|
+ <select name="season" class="select-css" style="margin: 0;" onchange="this.form.submit()">
|
|
|
+ <? foreach ($availableSeasons as $s) { ?>
|
|
|
+ <option value="<? echo $s ?>" <? echo isSelected($s, $season) ?>>
|
|
|
+ <? echo $s ?>
|
|
|
+ </option>
|
|
|
+ <? } ?>
|
|
|
+ </select>
|
|
|
+ <select name="csv_file" class="select-css" style="margin: 0;" onchange="this.form.submit()">
|
|
|
+ <? foreach ($energyPoints as $key => $file) { ?>
|
|
|
+ <option value="<? echo $key ?>" <? echo isSelected($key, $selected_csv) ?>>
|
|
|
+ <? echo $key ?>
|
|
|
+ </option>
|
|
|
+ <? } ?>
|
|
|
+ </select>
|
|
|
+ </div>
|
|
|
</form>
|
|
|
|
|
|
|
|
|
<script>
|
|
|
- function makeplot(){
|
|
|
- Plotly.d3.csv('<?echo $url_total_info;?>', (allRows)=>{
|
|
|
- const {mean_energy, mean_spread, mean_energy_stat_err} = parseResultsTable(allRows, "<?echo $selected_csv;?>");
|
|
|
- Plotly.d3.csv('<?echo $url.$selected_csv;?>', function(data){processData(data, mean_energy, mean_spread, mean_energy_stat_err)});
|
|
|
- }
|
|
|
- );
|
|
|
+ function makeplot() {
|
|
|
+ Plotly.d3.csv('<? echo $season_url; ?>', (allRows) => {
|
|
|
+ const {
|
|
|
+ mean_energy,
|
|
|
+ mean_spread,
|
|
|
+ mean_energy_stat_err
|
|
|
+ } = parseResultsTable(allRows, "<? echo $selected_csv; ?>");
|
|
|
+ Plotly.d3.csv('<? echo $energyPoints[$selected_csv]; ?>', function(data) {
|
|
|
+ processData(data, mean_energy, mean_spread, mean_energy_stat_err)
|
|
|
+ });
|
|
|
+ });
|
|
|
}
|
|
|
-
|
|
|
- function parseResultsTable(allRows, energy_point){
|
|
|
+
|
|
|
+ function parseResultsTable(allRows, energy_point) {
|
|
|
// Extracts a row following the energy point in the total csv file (or null if energy point is not exists)
|
|
|
data = energy_point.slice(0, -4).split('_');
|
|
|
- for (var i=0; i<allRows.length; i++){
|
|
|
- if (allRows[i].first_run == data[1] ){
|
|
|
+ for (var i = 0; i < allRows.length; i++) {
|
|
|
+ if (allRows[i].first_run == data[1]) {
|
|
|
return allRows[i];
|
|
|
}
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
- function parseRow(row){
|
|
|
+
|
|
|
+ function parseRow(row) {
|
|
|
// Parses a row from the detailed csv file
|
|
|
- row['start_time'] = Date.parse(row['compton_start']);
|
|
|
+ row['start_time'] = Date.parse(row['compton_start']);
|
|
|
row['stop_time'] = Date.parse(row['compton_stop']);
|
|
|
- row['center_time'] = new Date((row['start_time'] + row['stop_time'])/2);
|
|
|
- row['timedelta'] = (row['stop_time'] - row['start_time'])/2/1000; // in seconds
|
|
|
+ row['center_time'] = new Date((row['start_time'] + row['stop_time']) / 2);
|
|
|
+ row['timedelta'] = (row['stop_time'] - row['start_time']) / 2 / 1000; // in seconds
|
|
|
|
|
|
- text_energy = "E = " + parseFloat(row['e_mean']).toFixed(2) + "±" + parseFloat(row['e_std']).toFixed(2) + " MeV<br>";
|
|
|
- text_spread = "spread = " + parseFloat(row['spread_mean']).toFixed(2) + "±" + parseFloat(row['spread_std']).toFixed(2) + " MeV<br>";
|
|
|
- text_str = text_energy + text_spread + "<b>Compton</b><br>" + "<i>Start: </i>" +
|
|
|
- row['compton_start'] + "<br><i>Stop: </i>" + row['compton_stop'] + "<br><br>";
|
|
|
+ text_energy = "E = " + parseFloat(row['e_mean']).toFixed(2) + "±" + parseFloat(row['e_std']).toFixed(2) + " MeV<br>";
|
|
|
+ text_spread = "spread = " + parseFloat(row['spread_mean']).toFixed(2) + "±" + parseFloat(row['spread_std']).toFixed(2) + " MeV<br>";
|
|
|
+ text_str = text_energy + text_spread + "<b>Compton</b><br>" + "<i>Start: </i>" +
|
|
|
+ row['compton_start'] + "<br><i>Stop: </i>" + row['compton_stop'] + "<br><br>";
|
|
|
row['text_str'] = text_str + "<b>Runs: </b>" + row['run_first'] + " - " + row['run_last'] + "<br><br>";
|
|
|
return row;
|
|
|
}
|
|
|
-
|
|
|
- function processSpread(data, elementId, mean_value){
|
|
|
- let x = [], y = [], std_y = []
|
|
|
- for (var i=0; i<data.length; i++){
|
|
|
- const {center_time, spread_mean, spread_std} = parseRow(data[i]);
|
|
|
+
|
|
|
+ function processSpread(data, elementId, mean_value) {
|
|
|
+ let x = [],
|
|
|
+ y = [],
|
|
|
+ std_y = []
|
|
|
+ for (var i = 0; i < data.length; i++) {
|
|
|
+ const {
|
|
|
+ center_time,
|
|
|
+ spread_mean,
|
|
|
+ spread_std
|
|
|
+ } = parseRow(data[i]);
|
|
|
x.push(center_time);
|
|
|
y.push(spread_mean);
|
|
|
std_y.push(spread_std);
|
|
|
}
|
|
|
makeSpreadPlot(elementId, x, y, std_y, mean_value);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
function kde(x, y, w) {
|
|
|
- const ts = (t) => t.getTime()/1000;
|
|
|
+ const ts = (t) => t.getTime() / 1000;
|
|
|
const toDateTime = (secs) => {
|
|
|
let t = new Date(0);
|
|
|
t.setSeconds(secs);
|
|
|
return t;
|
|
|
};
|
|
|
const steps = 1000;
|
|
|
- const dt = (ts(x[x.length - 1]) - ts(x[0]))/steps;
|
|
|
+ const dt = (ts(x[x.length - 1]) - ts(x[0])) / steps;
|
|
|
const kernel = (x, x0, w0, y0) => {
|
|
|
- if (Math.abs(x-x0)>w0){
|
|
|
+ if (Math.abs(x - x0) > w0) {
|
|
|
return 0;
|
|
|
}
|
|
|
- return y0/w0;
|
|
|
+ return y0 / w0;
|
|
|
//return y0*3*(1-((x-x0)/w0/2)**2)/4;
|
|
|
- };
|
|
|
+ };
|
|
|
const get_est = (timestamp) => {
|
|
|
let val = 0
|
|
|
- for (var i=0; i<x.length; i++){
|
|
|
+ for (var i = 0; i < x.length; i++) {
|
|
|
val += kernel(timestamp, ts(x[i]), w[i], y[i]);
|
|
|
}
|
|
|
return val;
|
|
|
};
|
|
|
//console.log(x, y);
|
|
|
- const timestamp_arr = Plotly.d3.range(steps).map(function(i){return ts(x[0])+i*dt;});
|
|
|
-
|
|
|
+ const timestamp_arr = Plotly.d3.range(steps).map(function(i) {
|
|
|
+ return ts(x[0]) + i * dt;
|
|
|
+ });
|
|
|
+
|
|
|
let kdex = [];
|
|
|
let kdey = [];
|
|
|
- for (var j=0; j<timestamp_arr.length; j++){
|
|
|
+ for (var j = 0; j < timestamp_arr.length; j++) {
|
|
|
kdex.push(toDateTime(timestamp_arr[j]));
|
|
|
kdey.push(get_est(timestamp_arr[j]));
|
|
|
}
|
|
|
//console.log(kdex, kdey);
|
|
|
return [kdex, kdey]
|
|
|
}
|
|
|
-
|
|
|
- function oldAverage(E, L){
|
|
|
+
|
|
|
+ function oldAverage(E, L) {
|
|
|
//Averager by the old method with E and L only
|
|
|
- if (E.length !== L.length){
|
|
|
+ if (E.length !== L.length) {
|
|
|
return null;
|
|
|
}
|
|
|
let EL = 0;
|
|
|
let sL = 0;
|
|
|
- for (let i = 0; i<E.length; i++){
|
|
|
- EL += parseFloat(E[i])*parseFloat(L[i]);
|
|
|
+ for (let i = 0; i < E.length; i++) {
|
|
|
+ EL += parseFloat(E[i]) * parseFloat(L[i]);
|
|
|
sL += parseFloat(L[i]);
|
|
|
}
|
|
|
- return EL/sL;
|
|
|
+ return EL / sL;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
function processData(allRows, mean_energy, mean_spread, mean_energy_stat_err) {
|
|
|
// Processes all data rows
|
|
|
var dict = {};
|
|
@@ -177,44 +168,44 @@ $selected_csv = isset($_GET["csv_file"])&&in_array($_GET["csv_file"], $cleanArrs
|
|
|
dict['compton'] = [];
|
|
|
dict['lum'] = [];
|
|
|
dict['twidth'] = [];
|
|
|
-
|
|
|
- for (var i=0; i<allRows.length; i++){
|
|
|
+
|
|
|
+ for (var i = 0; i < allRows.length; i++) {
|
|
|
const row = parseRow(allRows[i]);
|
|
|
-
|
|
|
- dict['x'].push( row['center_time'] );
|
|
|
- dict['e_mean'].push( row['e_mean'] );
|
|
|
- dict['e_std'].push( row['e_std'] );
|
|
|
- dict['spread_mean'].push( row['spread_mean'] );
|
|
|
- dict['spread_std'].push( row['spread_std'] );
|
|
|
- dict['compton'].push( row['text_str'] );
|
|
|
- dict['lum'].push( row['luminosity'] );
|
|
|
- dict['twidth'].push( row['timedelta'] );
|
|
|
+
|
|
|
+ dict['x'].push(row['center_time']);
|
|
|
+ dict['e_mean'].push(row['e_mean']);
|
|
|
+ dict['e_std'].push(row['e_std']);
|
|
|
+ dict['spread_mean'].push(row['spread_mean']);
|
|
|
+ dict['spread_std'].push(row['spread_std']);
|
|
|
+ dict['compton'].push(row['text_str']);
|
|
|
+ dict['lum'].push(row['luminosity']);
|
|
|
+ dict['twidth'].push(row['timedelta']);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const [a, b] = kde(dict['x'], dict['lum'], dict['twidth']);
|
|
|
dict['kdex'] = a;
|
|
|
dict['kdey'] = b;
|
|
|
//console.log(dict['kdex'], dict['kdey']);
|
|
|
//oldAverage(y, dict['lum']);
|
|
|
-
|
|
|
+
|
|
|
dict['mean_energy_total'] = mean_energy;
|
|
|
dict['old_mean_energy_total'] = oldAverage(dict['e_mean'], dict['lum']);
|
|
|
dict['mean_spread_total'] = mean_spread;
|
|
|
- dict['mean_energy_stat_err'] = mean_energy_stat_err;
|
|
|
-
|
|
|
+ dict['mean_energy_stat_err'] = mean_energy_stat_err;
|
|
|
+
|
|
|
makePlotly(dict, "gd");
|
|
|
}
|
|
|
-
|
|
|
- function makePlotly(dict, elementId){
|
|
|
+
|
|
|
+ function makePlotly(dict, elementId) {
|
|
|
const getYRange = (y, std_y) => {
|
|
|
const ys = [...y].sort();
|
|
|
const std_ys = [...std_y].sort();
|
|
|
- let idx = Math.floor(ys.length/2);
|
|
|
+ let idx = Math.floor(ys.length / 2);
|
|
|
const y0 = parseFloat(ys[idx]);
|
|
|
const std0 = parseFloat(std_ys[idx]);
|
|
|
- return [y0-6*std0, y0+6*std0];
|
|
|
+ return [y0 - 6 * std0, y0 + 6 * std0];
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
var trace1 = {
|
|
|
x: dict['x'],
|
|
|
y: dict['e_mean'],
|
|
@@ -268,32 +259,38 @@ $selected_csv = isset($_GET["csv_file"])&&in_array($_GET["csv_file"], $cleanArrs
|
|
|
type: "scatter",
|
|
|
};
|
|
|
var traces = [trace1, trace2, trace3];
|
|
|
-
|
|
|
+
|
|
|
var updatemenus = [];
|
|
|
- console.log(dict);
|
|
|
- if (dict['mean_energy_total']){
|
|
|
+ console.log(dict);
|
|
|
+ if (dict['mean_energy_total']) {
|
|
|
updatemenus = [{
|
|
|
- buttons: [
|
|
|
- {
|
|
|
- args:[{'shapes[0].visible': true, 'shapes[1].visible': false, 'title': '<E> = ' + parseFloat(dict['mean_energy_total']).toFixed(3) + '±' + parseFloat(dict['mean_energy_stat_err']).toFixed(3) + ' MeV, ' + '<Spread> = ' + parseFloat(dict['mean_spread_total']).toFixed(3) + ' MeV',}],
|
|
|
+ buttons: [{
|
|
|
+ args: [{
|
|
|
+ 'shapes[0].visible': true,
|
|
|
+ 'shapes[1].visible': false,
|
|
|
+ 'title': '<E> = ' + parseFloat(dict['mean_energy_total']).toFixed(3) + '±' + parseFloat(dict['mean_energy_stat_err']).toFixed(3) + ' MeV, ' + '<Spread> = ' + parseFloat(dict['mean_spread_total']).toFixed(3) + ' MeV',
|
|
|
+ }],
|
|
|
label: 'Current average method',
|
|
|
method: 'relayout'
|
|
|
}, {
|
|
|
- args:[{'shapes[0].visible': false, 'shapes[1].visible': true, 'title': '<E> = ' + parseFloat(dict['old_mean_energy_total']).toFixed(3) + ' MeV, ' + '<Spread> = ' + parseFloat(dict['mean_spread_total']).toFixed(3) + ' MeV',}],
|
|
|
+ args: [{
|
|
|
+ 'shapes[0].visible': false,
|
|
|
+ 'shapes[1].visible': true,
|
|
|
+ 'title': '<E> = ' + parseFloat(dict['old_mean_energy_total']).toFixed(3) + ' MeV, ' + '<Spread> = ' + parseFloat(dict['mean_spread_total']).toFixed(3) + ' MeV',
|
|
|
+ }],
|
|
|
label: 'Former average method',
|
|
|
method: 'relayout'
|
|
|
- },
|
|
|
- ],
|
|
|
- direction: 'center',
|
|
|
- showactive: 'true',
|
|
|
- type: 'dropdown',
|
|
|
- y: 1.1,
|
|
|
- xanchor: 'left',
|
|
|
- yanchor: 'top',
|
|
|
- active: 0,
|
|
|
- }];
|
|
|
+ }, ],
|
|
|
+ direction: 'center',
|
|
|
+ showactive: 'true',
|
|
|
+ type: 'dropdown',
|
|
|
+ y: 1.1,
|
|
|
+ xanchor: 'left',
|
|
|
+ yanchor: 'top',
|
|
|
+ active: 0,
|
|
|
+ }];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
var layout = {
|
|
|
title: '<E> = ' + parseFloat(dict['mean_energy_total']).toFixed(3) + '±' + parseFloat(dict['mean_energy_stat_err']).toFixed(3) + ' MeV, ' + '<Spread> = ' + parseFloat(dict['mean_spread_total']).toFixed(3) + ' MeV',
|
|
|
updatemenus: updatemenus,
|
|
@@ -333,55 +330,59 @@ $selected_csv = isset($_GET["csv_file"])&&in_array($_GET["csv_file"], $cleanArrs
|
|
|
plot_bgcolor: 'rgba(0,0,0,0)',
|
|
|
autosize: true,
|
|
|
};
|
|
|
-
|
|
|
- if (dict['mean_energy_total']){
|
|
|
+
|
|
|
+ if (dict['mean_energy_total']) {
|
|
|
layout['shapes'] = [{
|
|
|
- type: 'line',
|
|
|
- yref: 'y3',
|
|
|
- xref: 'paper',
|
|
|
- x0: 0,
|
|
|
- x1: 1,
|
|
|
- y0: dict['mean_energy_total'],
|
|
|
- y1: dict['mean_energy_total'],
|
|
|
- line: {
|
|
|
- color: '#590A0A',
|
|
|
+ type: 'line',
|
|
|
+ yref: 'y3',
|
|
|
+ xref: 'paper',
|
|
|
+ x0: 0,
|
|
|
+ x1: 1,
|
|
|
+ y0: dict['mean_energy_total'],
|
|
|
+ y1: dict['mean_energy_total'],
|
|
|
+ line: {
|
|
|
+ color: '#590A0A',
|
|
|
+ },
|
|
|
},
|
|
|
- },
|
|
|
- {
|
|
|
- type: 'line',
|
|
|
- yref: 'y3',
|
|
|
- xref: 'paper',
|
|
|
- x0: 0,
|
|
|
- x1: 1,
|
|
|
- y0: dict['old_mean_energy_total'],
|
|
|
- y1: dict['old_mean_energy_total'],
|
|
|
- line: {
|
|
|
- color: '#590A0A',
|
|
|
- },
|
|
|
- visible: false,
|
|
|
- },
|
|
|
- {
|
|
|
- type: 'line',
|
|
|
- yref: 'y2',
|
|
|
- xref: 'paper',
|
|
|
- x0: 0,
|
|
|
- x1: 1,
|
|
|
- y0: dict['mean_spread_total'],
|
|
|
- y1: dict['mean_spread_total'],
|
|
|
- line: {
|
|
|
- color: '#590A0A',
|
|
|
+ {
|
|
|
+ type: 'line',
|
|
|
+ yref: 'y3',
|
|
|
+ xref: 'paper',
|
|
|
+ x0: 0,
|
|
|
+ x1: 1,
|
|
|
+ y0: dict['old_mean_energy_total'],
|
|
|
+ y1: dict['old_mean_energy_total'],
|
|
|
+ line: {
|
|
|
+ color: '#590A0A',
|
|
|
+ },
|
|
|
+ visible: false,
|
|
|
},
|
|
|
- visible: true,
|
|
|
- }];
|
|
|
+ {
|
|
|
+ type: 'line',
|
|
|
+ yref: 'y2',
|
|
|
+ xref: 'paper',
|
|
|
+ x0: 0,
|
|
|
+ x1: 1,
|
|
|
+ y0: dict['mean_spread_total'],
|
|
|
+ y1: dict['mean_spread_total'],
|
|
|
+ line: {
|
|
|
+ color: '#590A0A',
|
|
|
+ },
|
|
|
+ visible: true,
|
|
|
+ }
|
|
|
+ ];
|
|
|
}
|
|
|
-
|
|
|
- Plotly.newPlot('gd', traces, layout, {modeBarButtonsToRemove: ['toImage'], responsive: true,});
|
|
|
-
|
|
|
+
|
|
|
+ Plotly.newPlot('gd', traces, layout, {
|
|
|
+ modeBarButtonsToRemove: ['toImage'],
|
|
|
+ responsive: true,
|
|
|
+ });
|
|
|
+
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
makeplot();
|
|
|
-
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
-</html>
|
|
|
+
|
|
|
+</html>
|