"dependencies": {
"@nieuwlandgeo/sldreader": "0.3.x",
"chart.js": "4.x.x",
+ "chartjs-plugin-annotation": "^3.0.1",
"ol": "8.x.x"
},
"devDependencies": {
"pnpm": ">=7"
}
},
+ "node_modules/chartjs-plugin-annotation": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.0.1.tgz",
+ "integrity": "sha512-hlIrXXKqSDgb+ZjVYHefmlZUXK8KbkCPiynSVrTb/HjTMkT62cOInaT1NTQCKtxKKOm9oHp958DY3RTAFKtkHg==",
+ "peerDependencies": {
+ "chart.js": ">=4.0.0"
+ }
+ },
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"@kurkle/color": "^0.3.0"
}
},
+ "chartjs-plugin-annotation": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.0.1.tgz",
+ "integrity": "sha512-hlIrXXKqSDgb+ZjVYHefmlZUXK8KbkCPiynSVrTb/HjTMkT62cOInaT1NTQCKtxKKOm9oHp958DY3RTAFKtkHg==",
+ "requires": {}
+ },
"chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"dependencies": {
"@nieuwlandgeo/sldreader": "0.3.x",
"chart.js": "4.x.x",
+ "chartjs-plugin-annotation": "^3.0.1",
"ol": "8.x.x"
}
}
--- /dev/null
+import Chart from 'chart.js/auto';
+
+import ChartJsPart from './ChartJsPart.js';
+
+export default class BarChart extends ChartJsPart {
+ /** Constructor taking the mapName as an argument. */
+ constructor(chartName) {
+ super(chartName);
+ this.setChart(new Chart(this.getChartCanvas(), {
+ type: 'bar',
+ data: {
+ datasets: []
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ },
+ },
+ animation: false,
+ }
+ }));
+
+ }
+
+
+}
--- /dev/null
+import ChartPart from './ChartPart.js';
+
+import { Chart } from 'chart.js';
+import annotationPlugin from 'chartjs-plugin-annotation';
+
+Chart.register(annotationPlugin);
+
+export default class ChartJsPart extends ChartPart {
+ #chart;
+
+ /** Constructor taking the mapName as an argument. */
+ constructor(chartName) {
+ super(chartName);
+ }
+
+ setChart(chart) {
+ this.#chart = chart;
+ }
+
+ getChart() {
+ return this.#chart;
+ }
+
+ //
+ // DATA
+ //
+ setLabels(labels) {
+ const chart = this.getChart();
+ chart.data.labels = labels;
+ this.update();
+ }
+
+ addDataset(label, data) {
+ const chart = this.getChart();
+ chart.data.datasets.push({
+ label: label,
+ data: data,
+ borderWidth: 1
+ });
+ this.update();
+ }
+
+ setData(labels, label, data) {
+ this.clearDatasets();
+ this.setLabels(labels);
+ this.addDataset(label, data);
+ }
+
+ setDatasets(labels, datasets) {
+ const chart = this.getChart();
+ chart.data.datasets = datasets;
+ chart.data.labels = labels;
+ this.update();
+ }
+
+ clearDatasets() {
+ const chart = this.getChart();
+ chart.data.datasets = [];
+ this.update();
+ }
+
+ update() {
+ this.#chart.update();
+ }
+}
--- /dev/null
+/** API to be used by Java.
+ * @module MapPart
+ */
+
+/** Abstract base class for displaying a map. */
+export default class ChartPart {
+
+ /** The name of the chart, will also be the name of the variable */
+ #chartName;
+
+ constructor(chartName) {
+ this.#chartName = chartName;
+ this.createChartCanvas(this.#chartName);
+ }
+
+
+ //
+ // HTML
+ //
+ /** Create the div element where the chart will be displayed. */
+ createChartCanvas(id) {
+ const chartDiv = document.createElement('canvas');
+ chartDiv.id = id;
+ //chartDiv.style.cssText = 'width: 100%;';
+ chartDiv.style.cssText = 'width: 100%; height: 100vh;';
+ document.body.appendChild(chartDiv);
+ }
+
+ /** Get the div element where the chart is displayed. */
+ getChartCanvas() {
+ return document.getElementById(this.#chartName);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+import Chart from 'chart.js/auto';
+
+export default class TestGraph {
+
+ init() {
+ const ctx = document.getElementById('myChart');
+
+ new Chart(ctx, {
+ type: 'bar',
+ data: {
+ labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
+ datasets: [{
+ label: '# of Votes',
+ data: [12, 19, 3, 5, 2, 3],
+ borderWidth: 1
+ }]
+ },
+ options: {
+ scales: {
+ y: {
+ beginAtZero: true
+ }
+ }
+ }
+ });
+ }
+}
--- /dev/null
+import BarChart from './BarChart.js';
+import TestGraph from './TestGraph.js';
+//import { rectY, binX } from "@observablehq/plot";
+
+// PSEUDO PACKAGE
+if (typeof globalThis.argeo === 'undefined')
+ globalThis.argeo = {};
+if (typeof globalThis.argeo.app === 'undefined')
+ globalThis.argeo.app = {};
+if (typeof globalThis.argeo.app.chart === 'undefined')
+ globalThis.argeo.app.chart = {};
+
+// PUBLIC CLASSES
+globalThis.argeo.app.chart.BarChart = BarChart;
+globalThis.argeo.app.chart.TestGraph = TestGraph;
+
+//const plot = rectY({ length: 10000 }, binX({ y: "count" }, { x: Math.random })).plot();
+//const div = document.querySelector("#myplot");
+//div.append(plot);
+
+"use strict";
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+</head>
+
+<body>
+</body>
+
+</html>
\ No newline at end of file
--- /dev/null
+import './export-package.js';
+++ /dev/null
-import Chart from 'chart.js/auto';
-
-export default class TestGraph {
-
- init() {
- const ctx = document.getElementById('myChart');
-
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
- datasets: [{
- label: '# of Votes',
- data: [12, 19, 3, 5, 2, 3],
- borderWidth: 1
- }]
- },
- options: {
- scales: {
- y: {
- beginAtZero: true
- }
- }
- }
- });
- }
-}
+++ /dev/null
-import TestGraph from './TestGraph.js';
-//import { rectY, binX } from "@observablehq/plot";
-
-// PSEUDO PACKAGE
-if (typeof globalThis.argeo === 'undefined')
- globalThis.argeo = {};
-if (typeof globalThis.argeo.app === 'undefined')
- globalThis.argeo.app = {};
-if (typeof globalThis.argeo.app.graph === 'undefined')
- globalThis.argeo.app.graph = {};
-
-// PUBLIC CLASSES
-globalThis.argeo.app.graph.TestGraph = TestGraph;
-
-//const plot = rectY({ length: 10000 }, binX({ y: "count" }, { x: Math.random })).plot();
-//const div = document.querySelector("#myplot");
-//div.append(plot);
-
-"use strict";
+++ /dev/null
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
- <meta charset="UTF-8">
-</head>
-
-<body>
- <canvas id="myChart"></canvas>
-</body>
-
-</html>
\ No newline at end of file
+++ /dev/null
-import './export-package.js';
module.exports = {
entry: {
"geo": './src/geo/index.js',
- "graph": './src/graph/index.js',
+ "chart": './src/chart/index.js',
},
output: {
filename: '[name].[contenthash].js',
chunks: ['geo'],
}),
new HtmlWebpackPlugin({
- title: 'Argeo Suite Graph JS',
- template: 'src/graph/index.html',
+ title: 'Argeo Suite Chart JS',
+ template: 'src/chart/index.html',
scriptLoading: 'module',
- filename: 'graph.html',
- chunks: ['graph'],
+ filename: 'chart.html',
+ chunks: ['chart'],
}),
],
@Override
public Control createUiPart(Composite parent, Content context) {
- SwtJSMapPart map = new SwtJSMapPart("defaultOverviewMap", parent, 0);
+ SwtJsMapPart map = new SwtJsMapPart("defaultOverviewMap", parent, 0);
map.setCenter(13.404954, 52.520008); // Berlin
// map.setCenter(-74.00597, 40.71427); // NYC
// map.addPoint(-74.00597, 40.71427, null);
+++ /dev/null
-package org.argeo.app.geo.swt;
-
-import java.util.concurrent.CompletionStage;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import org.argeo.app.geo.GeoUtils;
-import org.argeo.app.geo.ux.JsImplementation;
-import org.argeo.app.geo.ux.MapPart;
-import org.argeo.app.swt.js.SwtBrowserJsPart;
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * An SWT implementation of {@link MapPart} based on JavaScript.
- */
-public class SwtJSMapPart extends SwtBrowserJsPart implements MapPart {
- static final long serialVersionUID = 2713128477504858552L;
-
- private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
- private final String mapName;// = "argeoMap";
-
- public SwtJSMapPart(String mapName, Composite parent, int style) {
- super(parent, style, "/pkg/org.argeo.app.js/geo.html");
- this.mapName = mapName;
- }
-
- @Override
- protected void init() {
- // create map
- doExecute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
- }
-
- /*
- * MapPart.js METHODS
- */
-
- @Override
- public void addPoint(double lng, double lat, String style) {
- callMapMethod("addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
- }
-
- @Override
- public void addUrlLayer(String url, GeoFormat format, String style) {
- callMapMethod("addUrlLayer('%s', '%s', %s, false)", url, format.name(), style);
- }
-
- public void addCssUrlLayer(String url, GeoFormat format, String css) {
- String style = GeoUtils.createSldFromCss("layer", "Layer", css);
- callMapMethod("addUrlLayer('%s', '%s', '%s', true)", url, format.name(), style);
- }
-
- @Override
- public void setZoom(int zoom) {
- callMapMethod("setZoom(%d)", zoom);
- }
-
- @Override
- public void setCenter(double lng, double lat) {
- callMapMethod("setCenter(%f, %f)", lng, lat);
- }
-
- protected CompletionStage<Object> callMapMethod(String methodCall, Object... args) {
- return callMethod(getJsMapVar(), methodCall, args);
- }
-
- private String getJsMapVar() {
- return getJsVarName(mapName);
- }
-
- /*
- * CALLBACKS
- */
- public void onFeatureSelected(Consumer<FeatureSelectedEvent> toDo) {
- addCallback("FeatureSelected", (arr) -> {
- toDo.accept(new FeatureSelectedEvent((String) arr[0]));
- return null;
- });
- }
-
- public void onFeatureSingleClick(Consumer<FeatureSingleClickEvent> toDo) {
- addCallback("FeatureSingleClick", (arr) -> {
- toDo.accept(new FeatureSingleClickEvent((String) arr[0]));
- return null;
- });
- }
-
- public void onFeaturePopup(Function<FeaturePopupEvent, String> toDo) {
- addCallback("FeaturePopup", (arr) -> {
- return toDo.apply(new FeaturePopupEvent((String) arr[0]));
- });
- }
-
- protected void addCallback(String suffix, Function<Object[], Object> toDo) {
- getReadyStage().thenAccept((ready) -> {
- String functionName = createJsFunction(mapName + "__on" + suffix, toDo);
- doExecute(getJsMapVar() + ".callbacks['on" + suffix + "']=" + functionName + ";");
- callMethod(mapName, "enable" + suffix + "()");
- });
- }
-}
--- /dev/null
+package org.argeo.app.geo.swt;
+
+import java.util.concurrent.CompletionStage;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.argeo.app.geo.GeoUtils;
+import org.argeo.app.geo.ux.JsImplementation;
+import org.argeo.app.geo.ux.MapPart;
+import org.argeo.app.swt.js.SwtBrowserJsPart;
+import org.eclipse.swt.widgets.Composite;
+
+/**
+ * An SWT implementation of {@link MapPart} based on JavaScript.
+ */
+public class SwtJsMapPart extends SwtBrowserJsPart implements MapPart {
+ static final long serialVersionUID = 2713128477504858552L;
+
+ private String jsImplementation = JsImplementation.OPENLAYERS_MAP_PART.getJsClass();
+ private final String mapName;// = "argeoMap";
+
+ public SwtJsMapPart(String mapName, Composite parent, int style) {
+ super(parent, style, "/pkg/org.argeo.app.js/geo.html");
+ this.mapName = mapName;
+ }
+
+ @Override
+ protected void init() {
+ // create map
+ doExecute(getJsMapVar() + " = new " + jsImplementation + "('" + mapName + "');");
+ }
+
+ /*
+ * MapPart.js METHODS
+ */
+
+ @Override
+ public void addPoint(double lng, double lat, String style) {
+ callMapMethod("addPoint(%f, %f, %s)", lng, lat, style == null ? "'default'" : style);
+ }
+
+ @Override
+ public void addUrlLayer(String url, GeoFormat format, String style) {
+ callMapMethod("addUrlLayer('%s', '%s', %s, false)", url, format.name(), style);
+ }
+
+ public void addCssUrlLayer(String url, GeoFormat format, String css) {
+ String style = GeoUtils.createSldFromCss("layer", "Layer", css);
+ callMapMethod("addUrlLayer('%s', '%s', '%s', true)", url, format.name(), style);
+ }
+
+ @Override
+ public void setZoom(int zoom) {
+ callMapMethod("setZoom(%d)", zoom);
+ }
+
+ @Override
+ public void setCenter(double lng, double lat) {
+ callMapMethod("setCenter(%f, %f)", lng, lat);
+ }
+
+ protected CompletionStage<Object> callMapMethod(String methodCall, Object... args) {
+ return callMethod(getJsMapVar(), methodCall, args);
+ }
+
+ private String getJsMapVar() {
+ return getJsVarName(mapName);
+ }
+
+ /*
+ * CALLBACKS
+ */
+ public void onFeatureSelected(Consumer<FeatureSelectedEvent> toDo) {
+ addCallback("FeatureSelected", (arr) -> {
+ toDo.accept(new FeatureSelectedEvent((String) arr[0]));
+ return null;
+ });
+ }
+
+ public void onFeatureSingleClick(Consumer<FeatureSingleClickEvent> toDo) {
+ addCallback("FeatureSingleClick", (arr) -> {
+ toDo.accept(new FeatureSingleClickEvent((String) arr[0]));
+ return null;
+ });
+ }
+
+ public void onFeaturePopup(Function<FeaturePopupEvent, String> toDo) {
+ addCallback("FeaturePopup", (arr) -> {
+ return toDo.apply(new FeaturePopupEvent((String) arr[0]));
+ });
+ }
+
+ protected void addCallback(String suffix, Function<Object[], Object> toDo) {
+ getReadyStage().thenAccept((ready) -> {
+ String functionName = createJsFunction(mapName + "__on" + suffix, toDo);
+ doExecute(getJsMapVar() + ".callbacks['on" + suffix + "']=" + functionName + ";");
+ callMethod(mapName, "enable" + suffix + "()");
+ });
+ }
+}
--- /dev/null
+package org.argeo.app.swt.chart;
+
+import org.argeo.app.swt.js.SwtBrowserJsPart;
+import org.eclipse.swt.widgets.Composite;
+
+/** Base class for charts. */
+public abstract class AbstractJsChart extends SwtBrowserJsPart {
+ private String chartName;
+
+ protected abstract String getJsImplementation();
+
+ public AbstractJsChart(String chartName, Composite parent, int style) {
+ super(parent, style, "/pkg/org.argeo.app.js/chart.html");
+ this.chartName = chartName;
+ }
+
+ @Override
+ protected void init() {
+ // create chart
+ doExecute(getJsChartVar() + " = new " + getJsImplementation() + "('" + chartName + "');");
+ }
+
+ protected String getJsChartVar() {
+ return getJsVarName(chartName);
+ }
+
+}
--- /dev/null
+package org.argeo.app.swt.chart;
+
+import java.io.StringWriter;
+
+import org.eclipse.swt.widgets.Composite;
+
+import jakarta.json.Json;
+import jakarta.json.stream.JsonGenerator;
+
+public class SwtJsBarChart extends AbstractJsChart {
+
+ public SwtJsBarChart(String chartName, Composite parent, int style) {
+ super(chartName, parent, style);
+ }
+
+ @Override
+ protected String getJsImplementation() {
+ return "globalThis.argeo.app.chart.BarChart";
+ }
+
+ public void setLabels(String[] labels) {
+ callMethod(getJsChartVar(), "setLabels(%s)", toJsArray(labels));
+ }
+
+ public void addDataset(String label, int[] values) {
+ callMethod(getJsChartVar(), "addDataset('%s', %s)", label, toJsArray(values));
+ }
+
+ public void setData(String[] labels, String label, int[] values) {
+ callMethod(getJsChartVar(), "setData(%s, '%s', %s)", toJsArray(labels), label, toJsArray(values));
+ }
+
+ public void setDatasets(String[] labels, String[] label, int[][] values) {
+ callMethod(getJsChartVar(), "setDatasets(%s, %s)", toJsArray(labels), toDatasets(label, values));
+ }
+
+ protected String toDatasets(String[] label, int[][] values) {
+ if (label.length != values.length)
+ throw new IllegalArgumentException("Arrays must have the same length");
+ StringWriter writer = new StringWriter();
+ JsonGenerator g = Json.createGenerator(writer);
+ g.writeStartArray();
+ for (int i = 0; i < label.length; i++) {
+ g.writeStartObject();
+ g.write("label", label[i]);
+ g.writeStartArray("data");
+ for (int j = 0; j < values[i].length; j++) {
+ g.write(values[i][j]);
+ }
+ g.writeEnd();// data array
+ g.writeEnd();// dataset
+ }
+ g.writeEnd();
+ g.close();
+ return writer.toString();
+ }
+
+ public void clearDatasets() {
+ callMethod(getJsChartVar(), "clearDatasets()");
+ }
+}
package org.argeo.app.swt.js;
+import java.util.Arrays;
import java.util.Locale;
+import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
return GLOBAL_THIS_ + name;
}
+ protected static String toJsArray(int... arr) {
+ return Arrays.toString(arr);
+ }
+
+ protected static String toJsArray(long... arr) {
+ return Arrays.toString(arr);
+ }
+
+ protected static String toJsArray(double... arr) {
+ return Arrays.toString(arr);
+ }
+
+ protected static String toJsArray(String... arr) {
+ return toJsArray((Object[]) arr);
+ }
+
+ protected static String toJsArray(Object... arr) {
+ StringJoiner sj = new StringJoiner(",", "[", "]");
+ for (Object o : arr) {
+ sj.add(toJsValue(o));
+ }
+ return sj.toString();
+ }
+
+ protected static String toJsValue(Object o) {
+ if (o instanceof CharSequence)
+ return '\"' + o.toString() + '\"';
+ else if (o instanceof Number)
+ return o.toString();
+ else if (o instanceof Boolean)
+ return o.toString();
+ else
+ return '\"' + o.toString() + '\"';
+ }
+
/*
* ACCESSORS
*/
-
+
public Control getControl() {
return browser;
}