Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Layer Config #430

Merged
merged 12 commits into from
Nov 21, 2023
3 changes: 2 additions & 1 deletion elements/jsonform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"typecheck": "tsc"
},
"dependencies": {
"@json-editor/json-editor": "^2.11.0"
"@json-editor/json-editor": "^2.11.0",
"toolcool-range-slider": "^4.0.28"
}
}
25 changes: 25 additions & 0 deletions elements/jsonform/src/custom-inputs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { JSONEditor } from "@json-editor/json-editor/dist/jsoneditor.js";
import { MinMaxEditor } from "./minmax";
const inputs = [
{
type: "object",
format: "minmax",
func: MinMaxEditor,
},
];

/**
* Add custom input fields to @json-editor
* @param {{[key: string]: any}} startVals
*/
export const addCustomInputs = (startVals) => {
inputs.map(({ type, format, func }) => {
JSONEditor.defaults["startVals"] = startVals;
JSONEditor.defaults.editors[format] = func;
JSONEditor.defaults.resolvers.unshift((schema) => {
if (schema.type === type && schema.format === format) return format;
});
});
};

export default addCustomInputs;
99 changes: 99 additions & 0 deletions elements/jsonform/src/custom-inputs/minmax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { AbstractEditor } from "@json-editor/json-editor/src/editor.js";
import "toolcool-range-slider/dist/plugins/tcrs-generated-labels.min.js";
import "toolcool-range-slider";

/**
* Set multiple attribute to element
* @param {Element} element
* @param {{[key: string]: any}} attributes
*/
function setAttributes(element, attributes) {
Object.keys(attributes).forEach((attr) => {
element.setAttribute(attr, attributes[attr]);
});
}

export class MinMaxEditor extends AbstractEditor {
register() {
super.register();
}

unregister() {
super.unregister();
}

build() {
const properties = this.schema.properties;
const options = this.options;
const description = this.schema.description;
const theme = this.theme;
const startVals = this.defaults.startVals[this.key];

if (!options.compact)
this.header = this.label = theme.getFormInputLabel(
this.getTitle(),
this.isRequired()
);
if (description)
this.description = theme.getFormInputDescription(
this.translateProperty(description)
);
if (options.infoText)
this.infoButton = theme.getInfoButton(
this.translateProperty(options.infoText)
);

const range = document.createElement("tc-range-slider");
// TODO - better logic to find min & max properties?
const minKey = Object.keys(properties).find((k) => k.includes("min"));
const maxKey = Object.keys(properties).find((k) => k.includes("max"));

const attributes = {
min: properties[minKey].minimum,
max: properties[maxKey].maximum,
value1: startVals?.[minKey] || properties[minKey].default,
value2: startVals?.[maxKey] || properties[maxKey].default,
"slider-bg-fill": "#004170",
"generate-labels": "true",
"slider-width": "100%",
"range-dragging": "false",
};
setAttributes(range, attributes);

this.input = range;
this.input.id = this.formname;
this.control = theme.getFormControl(
this.label,
this.input,
this.description,
this.infoButton
);

if (this.schema.readOnly || this.schema.readonly) {
this.disable(true);
this.input.disabled = true;
}

this.input.addEventListener("change", (e) => {
e.preventDefault();
e.stopPropagation();
this.value = {
[minKey]: e.detail.value1,
[maxKey]: e.detail.value2,
};
this.onChange(true);
});

this.container.appendChild(this.control);
}

destroy() {
if (this.label && this.label.parentNode)
this.label.parentNode.removeChild(this.label);
if (this.description && this.description.parentNode)
this.description.parentNode.removeChild(this.description);
if (this.input && this.input.parentNode)
this.input.parentNode.removeChild(this.input);
super.destroy();
}
}
33 changes: 21 additions & 12 deletions elements/jsonform/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { JSONEditor } from "@json-editor/json-editor/dist/jsoneditor.js";
import { LitElement, html } from "lit";
import { style } from "./style";
import { styleEOX } from "./style.eox";
import addCustomInputs from "./custom-inputs";

/**
* @typedef {JSON & {properties: object}} JsonSchema
Expand Down Expand Up @@ -88,10 +89,10 @@ export class EOxJSONForm extends LitElement {
return this.#data;
}

/**
* Data object has been changed
*/
#emitData() {
/**
* Data object has been changed
*/
this.dispatchEvent(
new CustomEvent(`change`, {
detail: this.#data,
Expand All @@ -102,8 +103,24 @@ export class EOxJSONForm extends LitElement {
this.requestUpdate();
}

/**
* Dispatch same function for multiple event type
*/
#dispatchEvent() {
const events = ["ready", "change"];

events.map((evt) => {
this.#editor.on(evt, () => {
this.#data = this.#editor.getValue();
this.#emitData();
});
});
}

firstUpdated() {
if (!this.#editor) {
addCustomInputs(this.startVals || {});

const formEle = this.renderRoot.querySelector("form");

this.#editor = new JSONEditor(formEle, {
Expand All @@ -114,15 +131,7 @@ export class EOxJSONForm extends LitElement {
...this.options,
});

this.#editor.on("ready", () => {
this.#data = this.#editor.getValue();
this.#emitData();
});

this.#editor.on("change", () => {
this.#data = this.#editor.getValue();
this.#emitData();
});
this.#dispatchEvent();
}
}

Expand Down
4 changes: 4 additions & 0 deletions elements/jsonform/src/style.eox.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,8 @@ export const styleEOX = `
text-indent: 0;
line-height: initial;
}
tc-range-slider{
display: block;
margin: 0.5rem 0;
}
`;
75 changes: 74 additions & 1 deletion elements/layercontrol/layercontrol.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ export const ExpandedLayers = {
export const Tools = {
args: {},
render: () => html`
<p>Default tools: info, opacity, remove, sort</p>
<p>Default tools: info, opacity, config, remove, sort</p>
<eox-layercontrol for="eox-map#tools"></eox-layercontrol>
<hr />
<p>Only one tool: info</p>
Expand Down Expand Up @@ -364,6 +364,79 @@ export const Tools = {
`,
};

/**
* The "config" tool reads settings passed via the "layerConfig" property,
* e.g. rendering a from from a provided JSON schema that allows updating the
* source url parameters.
*/
export const LayerConfig = {
args: {},
render: () => html`
<eox-layercontrol
.tools=${["config"]}
for="eox-map#config"
></eox-layercontrol>
<hr />
<eox-map
center="[-7000000, -500000]"
zoom="4"
id="config"
style="width: 400px; height: 300px;"
layers=${JSON.stringify([
{
type: "Tile",
properties: {
id: "customId",
title: "Tile XYZ",
layerConfig: {
schema: {
type: "object",
properties: {
vminmax: {
type: "object",
properties: {
vmin: {
type: "number",
minimum: 0,
maximum: 10,
format: "range",
},
vmax: {
type: "number",
minimum: 0,
maximum: 10,
format: "range",
},
},
format: "minmax",
},
cbar: {
type: "string",
enum: ["rain", "temperature"],
},
},
},
},
},
source: {
type: "XYZ",
url: "https://reccap2.api.dev.brockmann-consult.de/api/tiles/cop28~reccap2-9x108x139-0.0.1.zarr/deforested_biomass/{z}/{y}/{x}?crs=EPSG:3857&time=2018-01-01T00:00:00Z&vmin=0&vmax=3&cbar=rain",
params: { LAYERS: "topp:states", TILED: true },
ratio: 1,
serverType: "geoserver",
},
},
{
type: "Tile",
source: { type: "OSM" },
properties: { title: "Base Map" },
},
])}
>
</eox-map>
`,
};

/**
* By adding the `layerControlHide` property to map layers,
* they aren't displayed in the layer control at all (but may
Expand Down
67 changes: 46 additions & 21 deletions elements/layercontrol/src/components/layerConfig.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LitElement, html } from "lit";
import { live } from "lit/directives/live.js";
import "../../../jsonform/src/main";
import { getStartVals, updateUrl } from "../helpers";

/**
* Layer configuration for an individual layer
Expand All @@ -11,8 +12,21 @@ export class EOxLayerControlLayerConfig extends LitElement {
layer: { attribute: false },
unstyled: { type: Boolean },
noShadow: { type: Boolean },
silvester-pari marked this conversation as resolved.
Show resolved Hide resolved
layerConfig: { attribute: false },
};

/**
* data input by the user
* @type {{[key: string]: any}}
*/
#data = {};

/**
* data input by the user
* @type {{[key: string]: any}}
*/
#startVals = null;

constructor() {
super();

Expand All @@ -32,39 +46,50 @@ export class EOxLayerControlLayerConfig extends LitElement {
* Renders the element without a shadow root
*/
this.noShadow = true;
silvester-pari marked this conversation as resolved.
Show resolved Hide resolved

/**
* Layer config for eox-jsonform
* @type {{ schema: object, element: string }}
*/
this.layerConfig = null;
}

/**
* Handle eox-jsonform change
* @param {{ detail: { value: string; }; }} e
*/
#handleDataChange(e) {
this.#data = e.detail;
// @ts-ignore
const url = this.layer.getSource().getUrls()[0];

updateUrl(url, this.#data, this.layer);
this.requestUpdate();
}

createRenderRoot() {
silvester-pari marked this conversation as resolved.
Show resolved Hide resolved
return this.noShadow ? this : super.createRenderRoot();
}

render() {
if (!this.layerConfig) return ``;
this.#startVals = getStartVals(this.layer, this.layerConfig);

return html`
<style>
${this.#styleBasic}
${!this.unstyled && this.#styleEOX}
</style>
<input
type="range"
min="0"
max="1"
step="0.01"
value=${live(this.layer?.getOpacity())}
@input=${(/** @type {{ target: { value: string; }; }} */ evt) =>
this.layer.setOpacity(parseFloat(evt.target.value))}
/>
<button
class="delete"
@click=${() => {
this.layer?.set("layerControlOptional", true);
this.layer?.setVisible(false);
this.dispatchEvent(
new CustomEvent("changed", { detail: this.layer, bubbles: true })
);
<eox-jsonform
.schema=${this.layerConfig.schema}
.startVals=${this.#startVals}
.options=${{
disable_edit_json: true,
disable_collapse: true,
disable_properties: true,
}}
>
x
</button>
@change=${this.#handleDataChange}
></eox-jsonform>
`;
}

Expand Down
Loading
Loading