added geo widget

This commit is contained in:
dcore94 2024-06-03 16:22:08 +02:00
parent 16c8a6068b
commit 2e55fa7510
3 changed files with 217 additions and 1 deletions

View File

@ -13,6 +13,7 @@
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.cloud-dev.d4science.org/common/js/bss-min-1.2.6.js"></script>
<script src="../storage/d4s-storage.js"></script>
<script src="https://cdn.jsdelivr.net/npm/ol@v9.2.4/dist/ol.js"></script>
</head>
<body class="m-4">

View File

@ -43,6 +43,11 @@ class CCPInputWidgetController extends HTMLElement {
this.innerHTML += `<d4s-ccp-input-remotefile readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-remotefile>`
this.querySelector("d4s-ccp-input-remotefile").default = this.#data.schema.default
} else if (this.isGeo()) {
this.innerHTML += `<d4s-ccp-input-geo readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs}" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-remotefile>`
this.querySelector("d4s-ccp-input-geo").default = this.#data.schema.default
} else if (this.isSecret()) {
this.innerHTML += `<d4s-ccp-input-secret readonly="${this.#data.schema.readOnly}" default="${this.#data.schema.default}" maxOccurs="${this.#data.maxOccurs}" minOccurs="${this.#data.minOccurs }" name="${this.#data.id}" description="${btoa(this.#data.description)}" title="${this.#data.title}"></d4s-ccp-input-secret>`
@ -130,6 +135,13 @@ class CCPInputWidgetController extends HTMLElement {
(this.#data.schema.format.toLowerCase() === "remotefile")
}
isGeo() {
return (this.#data.schema.type === "string") &&
("format" in this.#data.schema) &&
(this.#data.schema.format != null) &&
(this.#data.schema.format.toLowerCase() === "geo")
}
isDateTime() {
return (this.#data.schema.type === "string") &&
("format" in this.#data.schema) &&
@ -770,4 +782,206 @@ class CCPSecretInputWidgetController extends CCPBaseInputWidgetController {
return out
}
}
window.customElements.define('d4s-ccp-input-secret', CCPSecretInputWidgetController);
window.customElements.define('d4s-ccp-input-secret', CCPSecretInputWidgetController);
class CCPGeoInputWidgetController extends CCPBaseInputWidgetController {
#format_dialog = null;
#target = null
#draw = null;
#raster = null
#source = null
#map = null;
#vector = null
#index = null;
#widget = null
constructor() {
super()
}
addToolContent() {
this.rootdoc.querySelector("div[name=tools]").innerHTML += `
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<svg name="trigger" style="width:24;height:24;fill:#007bff; cursor:pointer" viewBox="0 -960 960 960">
<path d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm-40-82v-78q-33 0-56.5-23.5T360-320v-40L168-552q-3 18-5.5 36t-2.5 36q0 121 79.5 212T440-162Zm276-102q20-22 36-47.5t26.5-53q10.5-27.5 16-56.5t5.5-59q0-98-54.5-179T600-776v16q0 33-23.5 56.5T520-680h-80v80q0 17-11.5 28.5T400-560h-80v80h240q17 0 28.5 11.5T600-440v120h40q26 0 47 15.5t29 40.5Z"/>
</svg>
<div name="map" class="d-none position-absolute shadow border border-primary bg-light p-2" style="right:0;z-index:1000; line-height:1.5rem;overflow:hidden;padding:5px;">
<div name="maptoolbar" class="mb-1" style="border-bottom: solid 1px gray;">
<div class="d-flex justify-content-between m-0">
<div>
<span title="Draw a circle" name="Circle" class="btn text-primary p-0 material-icons">radio_button_unchecked</span>
<span title="Select a Point" name="Point" class="btn text-primary p-0 material-icons">my_location</span>
<span title="Draw a Polygon" name="Polygon" class="btn text-primary p-0 material-icons">polyline</span>
<span title="Delete all" name="deletebtn" class="btn text-danger p-0 material-icons">delete</span>
</div>
<div>
<span title="Set geometry" class="btn btn-primary" style="font-weight:bold" name="selectbtn">Export</span>
<span title="Close" class="material-icons btn text-danger p-0" style="font-weight:bold" name="closebtn">close</span>
</div>
</div>
<small class="text-muted m-0">Draw and export geometry</small>
</div>
<div style="width:500px;height:500px;position:relative;">
<style>
#div[name=internalmap]{
width: 100%;
height:100%;
position: relative;
}
</style>
<div name="internalmap"></div>
</div>
</div>
<div class="modal fade" style="background-color:rgba(0,0,0,0.3)" name="format" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content shadow-lg border-primary">
<div class="modal-body">
Choose export format.
</div>
<div class="modal-footer">
<span title="Export WKT" name="wkt" class="btn btn-info">WKT</span>
<span title="Export to GML" name="gml" class="btn btn-primary">GML</span>
<span title="Export to GeoJSON" name="geojson" class="btn btn-secondary">GeoJSON</span>
<span title="Cancel" name="cancel" class="btn btn-danger">Cancel</span>
</div>
</div>
</div>
</div>
`
// Init map
this.init()
this.rootdoc.querySelector("div[name=maptoolbar]").addEventListener("click", ev=>{
const type = ev.target.getAttribute("name")
if(type === "closebtn"){
this.#widget.classList.add("d-none")
ev.preventDefault()
ev.stopPropagation()
}if(type === "selectbtn"){
this.#widget.classList.add("d-none")
this.#format_dialog.style.display = "block"
this.#format_dialog.classList.add("show")
this.#index = this.value.length - 1
const alltargets = this.#target = this.rootdoc.querySelectorAll("textarea")
for(let i=0; i < alltargets.length;i++){
if(!alltargets.item(i).value[i]){
this.#index = i
break;
}
}
this.#target = this.rootdoc.querySelectorAll("textarea").item(this.#index)
ev.preventDefault()
ev.stopPropagation()
}else if(["Circle", "Point", "Polygon"].indexOf(type) !== -1){
ev.stopPropagation();
ev.preventDefault();
if(this.#draw != null && this.#draw.type === type) return;
if(this.#draw != null) this.#map.removeInteraction(this.#draw);
this.#draw = new ol.interaction.Draw({source: this.#source, type : type})
this.#map.addInteraction(this.#draw)
}else if(type === "deletebtn"){
this.undoAllDrawings()
ev.preventDefault()
ev.stopPropagation()
}
})
document.addEventListener("keydown", ev=>{
if (ev.ctrlKey && ev.key === 'z') {
this.undoDrawing()
}else if(ev.key === "Escape"){
this.stopDrawing()
}
})
this.#format_dialog = this.rootdoc.querySelector("div.modal[name=format]")
this.#format_dialog.addEventListener("click", ev => {
ev.stopPropagation()
ev.preventDefault()
const features = this.#source.getFeatures()
if( features.length && this.#index != null && this.#target != null){
const name = ev.target.getAttribute("name")
var format
if(name === "wkt") {
format = new ol.format.WKT()
}else if(name === "geojson"){
format = new ol.format.GeoJSON()
}else if(name === "gml"){
format = new ol.format.GML()
}
const result = features.map(f=>{
const geom = f.getGeometry().getType() === "Circle" ? ol.geom.Polygon.fromCircle(f.getGeometry(), 50) : f.getGeometry()
return format.writeGeometry(geom)
}).join("\n")
this.#target.value = this.value[this.#index] = result
const newev = new Event("input", { bubbles : true})
this.dispatchEvent(newev)
}
this.#format_dialog.style.display = "none"
this.#format_dialog.classList.remove("show")
})
this.rootdoc.querySelector("svg[name=trigger]").addEventListener("click", ev => {
this.#widget.classList.toggle("d-none")
ev.preventDefault()
ev.stopPropagation()
})
}
init(){
this.#widget = this.rootdoc.querySelector("div[name=map]")
this.#draw = null
this.#raster = new ol.layer.Tile({source: new ol.source.OSM()});
this.#source = new ol.source.Vector({wrapX: false});
this.#vector = new ol.layer.Vector({source: this.#source});
this.#map = new ol.Map({
layers: [this.#raster, this.#vector],
target: this.rootdoc.querySelector("div[name=internalmap]"),
view: new ol.View({center: [0, 0], zoom: 4 }),
controls :[]
})
}
undoAllDrawings(){
this.#source.clear()
}
undoDrawing(){
const features = this.#source.getFeatures()
if(features.length){
this.#source.removeFeature(features[features.length-1])
}
}
stopDrawing(){
if(this.#draw){
this.#draw.abortDrawing()
this.#map.removeInteraction(this.#draw);
this.#draw = null
}
}
content() {
if(!this.readonly && !this.querySelector("div[name=map]")) this.addToolContent();
if (this.value.length <= 1) {
return `
<textarea name="${this.name}" data-index="0" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${this.value.length ? this.value[0] : ''}</textarea>
`
}
var out =
this.value.map((c, i) => {
return `
<textarea name="${this.name}" data-index="${i}" class="ccp-input my-2 form-control" placeholder="${this.title}" type="${this.type}" name="${this.name}" rows="5" ${this.readonly ? 'readonly' : ''} ${this.required ? 'required' : ''}>${c}</textarea>
`
}).join("\n")
return out
}
}
window.customElements.define('d4s-ccp-input-geo', CCPGeoInputWidgetController);

View File

@ -127,6 +127,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
<option value="code" ${this.isSelectedFormat('code') ? "selected" : ""}>Code</option>
<option value="file" ${this.isSelectedFormat('file') ? "selected" : ""}>File</option>
<option value="remotefile" ${this.isSelectedFormat('remotefile') ? "selected" : ""}>Workspace file</option>
<option value="geo" ${this.isSelectedFormat('geo') ? "selected" : ""}>Geography</option>
<option value="secret" ${this.isSelectedFormat('secret') ? "selected" : ""}>Secret</option>
<option value="url" ${this.isSelectedFormat('url') ? "selected" : ""}>Url</option>
</select>