From 04fdfab9e7a2ce4508e54a887abd3c8fd3936288 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 19 Jun 2023 00:11:36 +0700 Subject: [PATCH 01/46] Implement annotation editor on view screen --- apps/viewer/init.js | 39 +++ apps/viewer/uicallbacks.js | 31 +- common/LocalStore.js | 10 + core/Store.js | 27 ++ .../openseadragon-overlays-manage.js | 277 +++++++++++++++++- 5 files changed, 373 insertions(+), 11 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index 4bb0e76cc..be603eac8 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -280,6 +280,30 @@ function initCore() { $UI.annotPopup.open(e.position); }); + $CAMIC.viewer.addHandler('annot-edit-save', function (e) { + if (!e.data) { + return; + } + const {id, slide, data} = e; + const dataCopy = deepCopy(data); + delete dataCopy.selected; + delete dataCopy._id; + if (dataCopy.geometries.features[0].properties.size) { + const dataPoints = dataCopy.geometries.features[0].geometry.coordinates[0]; + const dataSize = dataCopy.geometries.features[0].properties.size; + const {points, bound, size} = convertToNormalized(dataPoints, dataSize, $CAMIC.viewer); + dataCopy.geometries.features[0].properties.size = size; + dataCopy.geometries.features[0].geometry.coordinates = [points] + dataCopy.geometries.features[0].bound.coordinates = [bound] + } else { + dataCopy.geometries = ImageFeaturesToVieweportFeatures( + $CAMIC.viewer, + dataCopy.geometries + ) + } + editAnnoCallback(id, slide, dataCopy); + }) + // create the message bar TODO for reading slide Info TODO $UI.slideInfos = new CaMessage({ /* opts that need to think of*/ @@ -307,6 +331,21 @@ function initCore() { }); } +function deepCopy(obj) { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + const copy = Array.isArray(obj) ? [] : {}; + for (let key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + copy[key] = deepCopy(obj[key]); + } + } + + return copy; +} + // initialize all UI components async function initUIcomponents() { /* create UI components */ diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index dc62328f5..429db96d2 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -942,6 +942,32 @@ function annoCallback(data) { .finally(() => {}); } +function editAnnoCallback(id, slide, annotJson) { + // save edit annotation + $CAMIC.store + .updateMaskEdit(id, slide, annotJson) + .then((data) => { + // server error + if (data.error) { + $UI.message.addError(`${data.text}:${data.url}`); + Loading.close(); + return; + } + + // no data added + if (data.count < 1) { + Loading.close(); + $UI.message.addWarning(`Edit Annotation Failed`); + return; + } + }) + .catch((e) => { + Loading.close(); + console.log('save failed', e); + }) + .finally(() => {}); +} + function saveAnnotCallback() { /* reset as default */ // clear draw data and UI @@ -988,7 +1014,6 @@ async function callback(data) { default: break; } - // console.log(data) // return; data.forEach(function(d) { @@ -1690,6 +1715,7 @@ function createHeatMapList(list) { async function addPresetLabelsHandler(label) { + console.log('label: ', label); const rs = await $CAMIC.store.addPresetLabels(label).then((d)=>d.result); if (rs.ok&&rs.nModified > 0) { @@ -1763,6 +1789,7 @@ function selectedPresetLabelsHandler(label) { } function drawLabel(e) { + console.log('run drawLabel'); if (!$CAMIC.viewer.canvasDrawInstance) { alert('Draw Doesn\'t Initialize'); return; @@ -1843,6 +1870,7 @@ function presetLabelOff() { } function savePresetLabel() { + console.log('savePresetLabel'); if ($CAMIC.viewer.canvasDrawInstance._path_index === 0) { // toast $UI.message.addWarning('info'+ @@ -2036,7 +2064,6 @@ function deleteRulerHandler(execId) { $CAMIC.store .deleteMarkByExecId(execId, $D.params.data.slide) .then((datas) => { - console.log(datas); // server error if (datas.error) { const errorMessage = `${datas.text}: ${datas.url}`; diff --git a/common/LocalStore.js b/common/LocalStore.js index eb8f2c699..a6eed6d97 100644 --- a/common/LocalStore.js +++ b/common/LocalStore.js @@ -174,6 +174,16 @@ function init_LocalStore(){ res(putInLocalStorage('mark', json)) }) } + Store.prototype.updateMaskEdit = function(maskId, slide, data) { + if (!this.validation.mark(data)){ + console.warn(this.validation.mark.errors) + } + return new Promise(function(res, rej){ + removeFromLocalStorage('mark', maskId).then(x => { + res(putInLocalStorage('mark', data)) + }) + }) + } Store.prototype.deleteMark = function(id, slide){ return new Promise((res, rej)=>{ res(removeFromLocalStorage('mark', id)) diff --git a/core/Store.js b/core/Store.js index 85cee4a8e..a2fdf8464 100644 --- a/core/Store.js +++ b/core/Store.js @@ -299,6 +299,33 @@ class Store { body: JSON.stringify(json), }).then(this.errorHandler); } + + /** + * update mark + * @param {maskId} the id of the mark + * @return {promise} - promise which resolves with response + **/ + updateMaskEdit(maskId, slide, data) { + const suffix = 'Mark/update'; + const url = this.base + suffix; + + if (this.validation.mark && !this.validation.mark(data)) { + console.warn(this.validation.mark.errors); + } + + const query = { + '_id': maskId, + 'provenance.image.slide': slide, + }; + + return fetch(url + '?' + objToParamStr(query), { + method: 'POST', + body: JSON.stringify(data), + credentials: 'include', + mode: 'cors', + }).then(this.errorHandler); + } + /** * delete mark * @param {object} id - the mark object id diff --git a/core/extension/openseadragon-overlays-manage.js b/core/extension/openseadragon-overlays-manage.js index 530c8cf04..9bdc5b39b 100644 --- a/core/extension/openseadragon-overlays-manage.js +++ b/core/extension/openseadragon-overlays-manage.js @@ -47,7 +47,7 @@ updateView:this.updateView.bind(this), zooming:this._zooming.bind(this), panning:this._panning.bind(this), - drawing:this._drawing.bind(this) + drawing:this._drawing.bind(this), } // -- create container div, and hover, display canvas -- // this._containerWidth = 0; @@ -81,6 +81,20 @@ this._hover_.style.left = 0; this._hover_ctx_ = this._hover_.getContext('2d'); this._div.appendChild(this._hover_); + // create edit_tool_canvas + this._edit_tool_ = document.createElement('canvas'); + this._edit_tool_.style.position = 'absolute'; + this._edit_tool_.style.top = 0; + this._edit_tool_.style.left = 0; + this._edit_tool_ctx_ = this._edit_tool_.getContext('2d'); + this._div.appendChild(this._edit_tool_); + // create edit_tool_hover_canvas + this._edit_tool_hover_ = document.createElement('canvas'); + this._edit_tool_hover_.style.position = 'absolute'; + this._edit_tool_hover_.style.top = 0; + this._edit_tool_hover_.style.left = 0; + this._edit_tool_hover_ctx_ = this._edit_tool_hover_.getContext('2d'); + this._div.appendChild(this._edit_tool_hover_); this._center = this._viewer.viewport.getCenter(true); this._interval = null; @@ -150,7 +164,12 @@ * @param {Event} e the event */ highlight:function(e){ - this._div.style.cursor = 'default'; + this.highlightEditPoint(e); + if (this.onEditPoint) { + this._div.style.cursor = 'pointer'; + } else { + this._div.style.cursor = 'default'; + } DrawHelper.clearCanvas(this._hover_); const point = new OpenSeadragon.Point(e.clientX, e.clientY); const img_point = this._viewer.viewport.windowToImageCoordinates(point); @@ -162,21 +181,28 @@ for(let j = 0;j < layer.data.length;j++){ const path = layer.data[j].geometry.path; const style = layer.data[j].properties.style; + const pathData = layer.data[j]; if(layer.hoverable&&path.contains(img_point.x,img_point.y)){ this.resize(); this.highlightPath = path; + this.highlightPathData = pathData; this.highlightStyle = style; this.highlightLayer = layer; this.highlightLayer.data.selected = j; - this.drawOnCanvas(this.drawOnHover,[this._hover_ctx_,this._div,path,style]); + this.currentHighlightIndex = i; + if (this.currentEditIndex !== this.currentHighlightIndex) { + this.drawOnCanvas(this.drawOnHover,[this._hover_ctx_,this._div,path,style]); + } return; }else{ this.highlightPath = null; this.highlightStyle = null; + this.highlightPathData = null; if(this.highlightLayer) { this.highlightLayer.data.selected = null; this.highlightLayer = null; } + this.currentHighlightIndex = null; } } } @@ -185,23 +211,29 @@ for(let j = 0;j < features.length;j++){ const path = features[j].geometry.path; const style = features[j].properties.style; + const pathData = features[j]; this.subIndex = null; if(layer.hoverable&&path&&path.contains(img_point.x,img_point.y)){ this.resize(); this.highlightPath = path; + this.highlightPathData = pathData; this.highlightStyle = style; this.highlightLayer = layer; this.highlightLayer.data.selected = j; - this.drawOnCanvas(this.drawOnHover,[this._hover_ctx_,this._div,path,style]); + this.currentHighlightIndex = i; + if (this.currentEditIndex !== this.currentHighlightIndex) { + this.drawOnCanvas(this.drawOnHover,[this._hover_ctx_,this._div,path,style]); + } return; }else{ this.highlightPath = null; this.highlightStyle = null; this.highlightLayer = null; + this.highlightPathData = null; + this.currentHighlightIndex = null; } } } - }, /** * @private @@ -209,8 +241,228 @@ * @param {Event} e the event */ pathClick:function(e){ - if(this.highlightLayer&&this.highlightLayer.clickable) - this._viewer.raiseEvent('canvas-lay-click',{position:{x:e.clientX, y:e.clientY},data:this.highlightLayer?this.highlightLayer.data:null}); + if(this.highlightLayer&&this.highlightLayer.clickable) { + if (this.currentEditIndex !== this.currentHighlightIndex) { + this._viewer.raiseEvent('canvas-lay-click',{position:{x:e.clientX, y:e.clientY}, data:this.highlightLayer?this.highlightLayer.data:null}); + if (this.currentEditIndex) { + this._viewer.raiseEvent('annot-edit-save',{id: this.overlays[this.currentEditIndex].data._id.$oid, slide: this.overlays[this.currentEditIndex].data.provenance.image.slide ,data:this.overlays[this.currentEditIndex].data}); + } + this.currentEditIndex = this.currentHighlightIndex; + } + this._viewer.setMouseNavEnabled(false); + this._div.addEventListener('mousemove', this.onEditPointMouseMove.bind(this)); + this._div.addEventListener('mouseout', this.onEditPointMouseUp.bind(this)); + this._div.addEventListener('mouseup', this.onEditPointMouseUp.bind(this)); + this._div.addEventListener('mousedown', this.onEditPointMouseDown.bind(this)); + } else { + try { + this._viewer.raiseEvent('annot-edit-save',{id: this.overlays[this.currentEditIndex].data._id.$oid, slide: this.overlays[this.currentEditIndex].data.provenance.image.slide ,data:this.overlays[this.currentEditIndex].data}); + } catch (error) {} + this.currentEditIndex = this.currentHighlightIndex; + this._viewer.setMouseNavEnabled(true); + this._div.removeEventListener('mousemove', this.onEditPointMouseMove.bind(this)); + this._div.removeEventListener('mouseout', this.onEditPointMouseUp.bind(this)); + this._div.removeEventListener('mouseup', this.onEditPointMouseUp.bind(this)); + this._div.removeEventListener('mousedown', this.onEditPointMouseDown.bind(this)); + } + this.editPathData = this.highlightPathData; + this.editPath = this.highlightPath; + this.editStyle = this.highlightStyle; + const editPointStyle = { + color: "#000000", + isFill: true, + lineCap: "round", + lineJoin: "round" + }; + this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this._div, this.editPathData, this.editPath, editPointStyle]); + this.updateView(); + }, + + drawEditPoints: function(ctx, div, pathData, path, style) { + if (!pathData) return; + if(style.isFill ==undefined || style.isFill){ + const imagingHelper = this._viewer.imagingHelper; + const lineWidth = (imagingHelper.physicalToDataX(1) - imagingHelper.physicalToDataX(0))>> 0; + ctx.lineWidth = lineWidth; + ctx.fillStyle = hexToRgbA(this.editStyle.color, 0.4); + ctx.strokeStyle = style.color; + path.stroke(ctx); + path.fill(ctx); + }else{ + const imagingHelper = this._viewer.imagingHelper; + const lineWidth = (imagingHelper.physicalToDataX(1) - imagingHelper.physicalToDataX(0))>> 0; + ctx.lineWidth = lineWidth; + ctx.fillStyle = hexToRgbA(this.editStyle.color, 0.4); + ctx.strokeStyle = style.color; + path.stroke(ctx); + } + + this.editPointPathList = []; + pathData = pathData.geometry.coordinates; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + ctx.fillStyle = hexToRgbA(style.color, 1); + ctx.strokeStyle = style.color; + ctx.lineWidth = style.lineWidth; + + if (this.editPathData.geometry.type === 'Point') { + const pointPath = new Path(); + pointPath.arc( + pathData[0], + pathData[1], + ctx.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(ctx); + this.editPointPathList.push(pointPath); + } else { + pathData[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + ctx.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(ctx); + this.editPointPathList.push(pointPath); + }) + } + }, + + highlightEditPoint: function(e) { + DrawHelper.clearCanvas(this._edit_tool_hover_); + if (!this.editPathData) return; + if (!this.editPointPathList) return; + const editPointStyle = { + color: "#000000", + isFill: true, + lineCap: "round", + lineJoin: "round" + }; + const point = new OpenSeadragon.Point(e.clientX, e.clientY); + const img_point = this._viewer.viewport.windowToImageCoordinates(point); + for(let i = 0;i> 0; + ctx.lineWidth = lineWidth; + path.stroke(ctx); + path.fill(ctx); + }, + + onEditPointMouseDown: function(e) { + const point = new OpenSeadragon.Point(e.clientX, e.clientY); + const img_point = this._viewer.viewport.windowToImageCoordinates(point); + for (let i = 0; i < this.editPointPathList.length; i++) { + if (this.editPointPathList[i].contains(img_point.x, img_point.y)) { + this.onEdit = true; + this.onEditIndex = i; + return; + } + } + this.onEdit = false; + this.onEditIndex = null; + }, + + onEditPointMouseMove: function(e) { + if (!this.onEdit) return; + DrawHelper.clearCanvas(this._edit_tool_); + const point = new OpenSeadragon.Point(e.clientX, e.clientY); + const img_point = this._viewer.viewport.windowToImageCoordinates(point); + // create new edit path data + this.editPointPathList = []; + if (this.editPathData.geometry.type === 'Polygon') { + // handle last point data + if (this.onEditIndex === 0 || this.onEditIndex === this.editPathData.geometry.coordinates[0].length - 1) { + this.editPathData.geometry.coordinates[0][0] = [img_point.x, img_point.y]; + this.editPathData.geometry.coordinates[0][this.editPathData.geometry.coordinates[0].length - 1] = [img_point.x, img_point.y]; + } else { + this.editPathData.geometry.coordinates[0][this.onEditIndex] = [img_point.x, img_point.y]; + } + this.editPathData.geometry.coordinates[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + }) + } else if (this.editPathData.geometry.type === 'Point') { + this.editPathData.geometry.coordinates[0] = img_point.x; + this.editPathData.geometry.coordinates[1] = img_point.y; + const pointPath = new Path(); + pointPath.arc( + this.editPathData.geometry.coordinates[0], + this.editPathData.geometry.coordinates[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + } else { + // brush + this.editPathData.geometry.coordinates[0][this.onEditIndex] = [img_point.x, img_point.y]; + this.editPathData.geometry.coordinates[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + }) + } + + DrawHelper.draw(this._edit_tool_ctx_, [this.editPathData]); + }, + + onEditPointMouseUp: function(e) { + if (this.editPathData?.geometry.type === 'LineString') { + if (!this.onEdit) return; + DrawHelper.clearCanvas(this._edit_tool_); + const point = new OpenSeadragon.Point(e.clientX, e.clientY); + const img_point = this._viewer.viewport.windowToImageCoordinates(point); + const size = this.editPathData.properties.size; + const topleft = [Math.floor(img_point.x/size[0])*size[0], Math.floor(img_point.y/size[1])*size[1]]; + this.editPathData.geometry.coordinates[0][this.onEditIndex] = topleft; + this.editPathData.geometry.coordinates[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + }) + } + this.onEdit = false; }, /** @@ -257,12 +509,15 @@ this._display_ctx_.lineWidth = (imagingHelper.physicalToDataX(1) - imagingHelper.physicalToDataX(0)) >> 0;; this._display_ctx_.viewBoundBoxInData = this.getViewBoundBoxInData() this._display_ctx_.imagingHelper = imagingHelper; + this._edit_tool_ctx_.radius = (imagingHelper.physicalToDataX(3) - imagingHelper.physicalToDataX(0)) >> 0; // if (this._containerWidth !== this._viewer.container.clientWidth) { this._containerWidth = this._viewer.container.clientWidth; this._div.setAttribute('width', this._containerWidth); this._hover_.setAttribute('width', this._containerWidth); this._display_.setAttribute('width', this._containerWidth); + this._edit_tool_.setAttribute('width', this._containerWidth); + this._edit_tool_hover_.setAttribute('width', this._containerWidth); } if (this._containerHeight !== this._viewer.container.clientHeight) { @@ -270,6 +525,8 @@ this._div.setAttribute('height', this._containerHeight); this._hover_.setAttribute('height', this._containerHeight); this._display_.setAttribute('height', this._containerHeight); + this._edit_tool_.setAttribute('height', this._containerHeight); + this._edit_tool_hover_.setAttribute('height', this._containerHeight); } this._viewportOrigin = new $.Point(0, 0); var boundsRect = this._viewer.viewport.getBounds(true); @@ -384,7 +641,7 @@ }; this.drawOnCanvas(this.drawOnDisplay,[this._display_ctx_]); if(this.highlightPath&& this.highlightLayer&& this.highlightLayer.hoverable)this.drawOnCanvas(this.drawOnHover,[this._hover_ctx_,this._div,this.highlightPath,this.highlightStyle]); - + // if(this.editPath&& this.highlightLayer&& this.editPathData)this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this._div, this.editPathData, this.editPath, this.editStyle]); }, /** @@ -429,7 +686,7 @@ return true; } - return false; + return false; }, /** * get Overlay by id @@ -458,6 +715,8 @@ clearCanvas:function(){ DrawHelper.clearCanvas(this._display_); DrawHelper.clearCanvas(this._hover_); + DrawHelper.clearCanvas(this._edit_tool_); + DrawHelper.clearCanvas(this._edit_tool_hover_); }, clear:function(){ From ce48249ec389b6be7695aff35f1979d2642101e4 Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Wed, 21 Jun 2023 20:30:04 +0100 Subject: [PATCH 02/46] Replace with sanitized file input --- apps/loader/chunked_upload.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/loader/chunked_upload.js b/apps/loader/chunked_upload.js index 8c9c16ccf..2e5f8ba0d 100644 --- a/apps/loader/chunked_upload.js +++ b/apps/loader/chunked_upload.js @@ -59,12 +59,13 @@ async function readFileChunks(file, token) { async function handleUpload(selectedFiles) { selectedFile = selectedFiles[0]; const filename = selectedFiles[0]['name']; - const token = await startUpload(filename); + const uploadMetadata = await startUpload(filename); + const token = uploadMetadata.upload_token; $('#tokenRow').show(300); const callback = continueUpload(token); readFileChunks(selectedFile, token); // parseFile(selectedFile, callback, 0, x=>(changeStatus("UPLOAD", "Finished Reading File"))) - updateFormOnUpload(selectedFiles[0]['name'], token); + updateFormOnUpload(uploadMetadata.filename, token); document.getElementById('fileUploadInput').colSpan = selectedFiles.length; document.getElementById('controlButtons').colSpan = selectedFiles.length+1; @@ -78,7 +79,7 @@ async function startUpload(filename) { try { const a = await token; changeStatus('UPLOAD', 'Begun upload - Token:' + a['upload_token']); - return a['upload_token']; + return { upload_token: a['upload_token'], filename: a['filename'] };; } catch (e) { changeStatus('UPLOAD | ERROR;', e); } @@ -130,6 +131,9 @@ function finishUpload() { regReq.then((x)=>x.json()).then((a)=>{ changeStatus('UPLOAD | Finished', a, reset); reset = false; console.log(a); + if (a.filepath) { + document.getElementById('filename'+0).value = a.filepath.slice(a.filepath.lastIndexOf('/')+1); + } if (typeof a === 'object' && a.error) { finishUploadSuccess = false; $('#check_btn').hide(); @@ -163,7 +167,7 @@ function finishUpload() { async function handleUrlUpload(url) { $('#uploadLoading').css('display', 'block'); - const token = await startUpload(url); + const token = (await startUpload(url)).upload_token; await continueUrlUpload(token, url); } From d1125b081092034ac55892b58c05883ec58a21b1 Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Wed, 21 Jun 2023 20:32:46 +0100 Subject: [PATCH 03/46] Validate on message content processing not on header Ran are: "headers are ok" and message content extraction. Run validation after the latter since this does not change behavior but we know the accepted file name only after this part, otherwise we have a race condition --- apps/loader/chunked_upload.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/loader/chunked_upload.js b/apps/loader/chunked_upload.js index 2e5f8ba0d..9d54c074b 100644 --- a/apps/loader/chunked_upload.js +++ b/apps/loader/chunked_upload.js @@ -147,6 +147,7 @@ function finishUpload() { $('#check_btn').show(); $('#post_btn').hide(); } + validateForm(CheckBtn); } }); regReq.then((e)=> { @@ -157,8 +158,6 @@ function finishUpload() { changeStatus('UPLOAD | ERROR;', e); reset = true; console.log(e); - } else { - validateForm(CheckBtn); } }, ); From 48e9166d4e5c3dbb689a48c81bcc39081368564c Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Thu, 22 Jun 2023 19:59:45 +0100 Subject: [PATCH 04/46] Likewise for batch loader --- apps/batchloader/batchLoader.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/batchloader/batchLoader.js b/apps/batchloader/batchLoader.js index 2e15cb803..7d91c0513 100644 --- a/apps/batchloader/batchLoader.js +++ b/apps/batchloader/batchLoader.js @@ -198,7 +198,7 @@ function checkNames() { } numErrors++; } else if (!allowedExtensions.includes(fileNames[i].substring(fileNames[i].lastIndexOf('.')+1, - fileNames[i].length))) { + fileNames[i].length).toLowerCase())) { let errorIcon = `   `; if ($('.fileNameEdit:eq('+i+')').prev().prev('#fileNameError').length == 0) { $('.fileNameEdit:eq('+i+')').parent().prepend(errorIcon); @@ -346,8 +346,11 @@ function finishBatch() { } async function handleUpload(selectedFile, filename, i) { - const token = await startUpload(filename); + const uploadMetadata = await startUpload(filename); + const token = uploadMetadata.upload_token; tokens.push(token); + fileNames[i] = uploadMetadata.filename; + $('tr:eq('+(i+1)+') td:nth-child(2) span')[0].innerText = uploadMetadata.filename; let j = 0; $('.token').each(function() { $(this).html(tokens[j++]); @@ -371,7 +374,7 @@ async function startUpload(filename) { }}).then((x)=>x.json()); try { const a = await token; - return a['upload_token']; + return { upload_token: a['upload_token'], filename: a['filename'] }; } catch (e) { console.log(e); } @@ -395,6 +398,11 @@ function finishUpload(token, filename, i) { }}); regReq.then((x)=>x.json()).then((a)=>{ // changeStatus('UPLOAD | Finished', a, reset); reset = false; + if (a.filepath) { + const newName = a.filepath.slice(a.filepath.lastIndexOf('/')+1); + fileNames[i] = newName; + $('tr:eq('+(i+1)+') td:nth-child(2) span')[0].innerText = newName; + } if (typeof a === 'object' && a.error) { finishUploadSuccess = false; // $('#check_btn').hide(); From 809f2b3c4999eb0f507b2cdf7ea38f20a2d00188 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 26 Jun 2023 20:53:59 +0700 Subject: [PATCH 05/46] Add annotation assistant UI --- apps/viewer/init.js | 354 +++++++++++++++++++- apps/viewer/uicallbacks.js | 8 + apps/viewer/viewer.html | 24 ++ components/ml-assistant/ml-assistant.css | 403 +++++++++++++++++++++++ components/ml-assistant/ml-assistant.js | 347 +++++++++++++++++++ components/sidemenu/sidemenu.css | 12 +- components/sidemenu/sidemenu.js | 65 +++- 7 files changed, 1198 insertions(+), 15 deletions(-) create mode 100644 components/ml-assistant/ml-assistant.css create mode 100644 components/ml-assistant/ml-assistant.js diff --git a/apps/viewer/init.js b/apps/viewer/init.js index 4bb0e76cc..ad7b22e3b 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -6,6 +6,38 @@ let $minorCAMIC = null; // for all instances of UI components const $UI = new Map(); +// INITIALIZE DB +window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; +// id(autoinc), name, location(name+id), classes +var request; var db; +var modelName; +var flag = -1; +var choices1; + +// tensorflowjs creates its own IndexedDB on saving a model. +async function dbInit() { + const model = tf.sequential(); + await model.save('indexeddb://dummy'); + await tf.io.removeModel('indexeddb://dummy'); + console.log('DB initialised'); +} + +// Opening the db created by tensorflowjs +function dbOpen() { + request = window.indexedDB.open('tensorflowjs', 1); + + request.onupgradeneeded = function(e) { + console.log('nasty!'); + }; + request.onerror = function(e) { + console.log('ERROR', e); + }; + request.onsuccess = function(e) { + db = request.result; + console.log('tfjs db opened and ready'); + }; +} + const $D = { pages: { home: '../table.html', @@ -89,10 +121,11 @@ window.addEventListener('keydown', (e) => { // initialize viewer page function initialize() { - var checkPackageIsReady = setInterval(function() { + var checkPackageIsReady = setInterval(async function() { if (IsPackageLoading) { clearInterval(checkPackageIsReady); // create a viewer and set up + await dbInit().then((e) => dbOpen()); initCore(); // loading the form template data @@ -632,6 +665,81 @@ async function initUIcomponents() { ], }); + // Create uploadModal for model uploads. + $UI.uploadModal = new ModalBox({ + id: 'upload_panel', + hasHeader: true, + headerText: 'Upload Model', + hasFooter: false, + provideContent: true, + content: ` +
+
    +
  • + + + Name of the model +
  • +
  • + + + The image size on which the model is trained (y x y) +
  • +
    + +
    + + +
  • + + + Magnification of input images +
  • +
    + +

    +
    Select model.json first followed by the weight binaries.

    +

    +

    +
    URL to the ModelAndWeightsConfig JSON describing the model.

    +

    + +
+ +
+ + + `, + }); + + // Create infoModal to show information about models uploaded. + $UI.infoModal = new ModalBox({ + id: 'model_info', + hasHeader: true, + headerText: 'Available Models', + hasFooter: false, + provideContent: true, + content: ` + + + + + + + + + + + + +
NameInput SizeSize (MB)Date SavedRemove Model
+ `, + }); + // TODO -- labels // $UI.labelsSideMenu = new SideMenu({ id: 'labels_layers', @@ -665,6 +773,23 @@ async function initUIcomponents() { // == end -- // + /* --- machine learning Assistant --- */ + $UI.AssistantSideMenu = new SideMenu({ + id: 'ml_assistant_layers', + width: 240, + contentPadding: 5, + position: 'right', + height: '30vh', + top: '30px' + }); + + var AssistantTitle = document.createElement('div'); + AssistantTitle.classList.add('item_head'); + AssistantTitle.textContent = 'Annotation Assistant'; + $UI.AssistantSideMenu.addContent(AssistantTitle); + + // == end == // + var checkOverlaysDataReady = setInterval(function() { if ( $D.params.data && @@ -701,6 +826,13 @@ async function initUIcomponents() { // return {item: d, isShow: false}; // }); + $UI.AssistantViewer = new Assistant({ + id: 'ml_assistant', + viewer: $CAMIC.viewer, + }); + $UI.AssistantViewer.elt.parentNode.removeChild($UI.AssistantViewer.elt); + $UI.AssistantSideMenu.addContent($UI.AssistantViewer.elt); + // create UI and set data $UI.layersViewer = createLayerViewer( 'overlayers', @@ -818,7 +950,227 @@ async function initUIcomponents() { $UI.appsSideMenu.addContent($UI.annotOptPanel.elt); } }, 300); + + // Handle event + $CAMIC.viewer.addHandler('open-add-model', () => { + uploadModel(); + }) + + $CAMIC.viewer.addHandler('open-model-info', () => { + showInfo(); + }) + } + +// Shows the uploaded models' details +async function showInfo() { + var data = await tf.io.listModels(); + var table = document.querySelector('#mdata'); + var tx = db.transaction('models_store', 'readonly'); + var store = tx.objectStore('models_store'); + var modelCount = 0; + empty(table); + // Update table data + (function(callback) { + for (const key in data) { + if (data.hasOwnProperty(key)) { + const name = key.split('/').pop(); + const date = data[key].dateSaved.toString().slice(0, 15); + const size = (data[key].modelTopologyBytes + data[key].weightDataBytes + + data[key].weightSpecsBytes) / (1024*1024); + const row = table.insertRow(); + let classes; let inputShape; let td; + + if (name.slice(0, 3) == 'seg') { + store.get(name).onsuccess = function(e) { + inputShape = e.target.result.input_shape.slice(1, 3).join('x'); + td = row.insertCell(); + td.innerText = name.split('_').splice(1).join('_').slice(0, -3); + td = row.insertCell(); + td.innerText = inputShape; + td = row.insertCell(); + td.innerText = +size.toFixed(2); + td = row.insertCell(); + td.innerText = date; + td = row.insertCell(); + td.innerHTML = ''; + document.getElementById('removeModel'+modelCount).addEventListener('click', () => { + deleteModel(name); + }); + modelCount += 1; + }; + } + } + } + callback; + })($UI.infoModal.open()); +} + + +async function deleteModel(name) { + deletedmodelName = name.split('/').pop().split('_').splice(2).join('_').slice(0, -3); + if (confirm('Are you sure you want to delete ' + deletedmodelName + ' model?')) { + const res = await tf.io.removeModel(IDB_URL + name); + console.log(res); + const tx = db.transaction('models_store', 'readwrite'); + const store = tx.objectStore('models_store'); + let status = false; + try { + store.delete(name); + status = true; + } catch (err) { + alert(err); + } finally { + if (status) { + let popups = document.getElementById('popup-container'); + if (popups.childElementCount < 2) { + let popupBox = document.createElement('div'); + popupBox.classList.add('popup-msg', 'slide-in'); + popupBox.innerHTML = `info` + deletedmodelName + ` model deleted successfully`; + popups.insertBefore(popupBox, popups.childNodes[0]); + setTimeout(function() { + popups.removeChild(popups.lastChild); + }, 3000); + } + $UI.infoModal.close(); + initUIcomponents(); + } + } + } else { + return; + } +} + +function uploadModel() { + var _name = document.querySelector('#name'); + var _imageSize = document.querySelector('#imageSize'); + var mag = document.querySelector('#magnification'); + var topology = document.querySelector('#modelupload'); + var weights = document.querySelector('#weightsupload'); + var status = document.querySelector('#status'); + var toggle = document.querySelector('#togBtn'); + var url = document.querySelector('#url'); + var refresh = document.querySelector('#refresh'); + var submit = document.querySelector('#submit'); + + // Reset previous input + _name.value = topology.value = weights.value = status.innerText = _imageSize.value = url.value = ''; + + $UI.uploadModal.open(); + + toggle.addEventListener('change', function(e) { + if (this.checked) { + document.querySelector('.checktrue').style.display = 'block'; + document.querySelector('.checkfalse').style.display = 'none'; + } else { + document.querySelector('.checktrue').style.display = 'none'; + document.querySelector('.checkfalse').style.display = 'block'; + } + }); + + refresh.addEventListener('click', () => { + initUIcomponents(); + }); + + submit.addEventListener('click', async function(e) { + e.preventDefault(); + + if ( _name.value && _imageSize.value && + ((!toggle.checked && topology.files[0].name.split('.').pop() == 'json') || (toggle.checked && url))) { + status.innerText = 'Uploading'; + status.classList.remove('error'); + status.classList.add('blink'); + + if (toggle.checked) { + var modelInput = url.value; + } else { + var modelInput = tf.io.browserFiles([topology.files[0], ...weights.files]); + } + + // Check if model with same name is previously defined + try { + if (modelName.indexOf(_name.value)!=-1) { + throw new Error('Model name repeated'); + } + } catch (e) { + status.innerText = 'Model with the same name already exists. Please choose a new name'; + status.classList.remove('blink'); + console.log(e); + return; + } + + try { + // This also ensures that valid model is uploaded. + // Adding some extra digits in the end to maintain uniqueness + const _channels = parseInt(document.querySelector('input[name="channels"]:checked').value); + const size = parseInt(_imageSize.value); + const name = 'seg-' + size.toString() + + '-' + mag.value.toString() + + '_' + _name.value + + (new Date().getTime().toString()).slice(-4, -1); + const model = await tf.loadLayersModel(modelInput); + const result = model.predict(tf.ones([1, size, size, _channels])); + const shape = result.shape; + result.dispose(); + if (shape[1] != size || shape[2] != size) { + console.info('Shape:', shape[1], shape[2]); + throw new Error('The application only supports 1:1 image Masks. Import a valid model.'); + } + + await model.save(IDB_URL + name); + + // Update the model store db entry to have the classes array + tx = db.transaction('models_store', 'readwrite'); + store = tx.objectStore('models_store'); + + store.get(name).onsuccess = function(e) { + const data = e.target.result; + data['input_shape'] = [1, size, size, _channels]; + + const req = store.put(data); + req.onsuccess = function(e) { + console.log('SUCCESS, ID:', e.target.result); + modelName.push(_name.value); + let popups = document.getElementById('popup-container'); + if (popups.childElementCount < 2) { + let popupBox = document.createElement('div'); + popupBox.classList.add('popup-msg', 'slide-in'); + popupBox.innerHTML = `info` + _name.value + ` model uploaded sucessfully`; + popups.insertBefore(popupBox, popups.childNodes[0]); + setTimeout(function() { + popups.removeChild(popups.lastChild); + }, 3000); + } + $UI.uploadModal.close(); + initUIcomponents(); + }; + req.onerror = function(e) { + status.innerText = 'Some error this way!'; + console.log(e); + status.classList.remove('blink'); + }; + }; + } catch (e) { + status.classList.add('error'); + status.classList.remove('blink'); + if (toggle.checked) { + status.innerText = 'Please enter a valid URL.'; + } else { + status.innerText = 'Please enter a valid model. ' + + 'Input model.json in first input and all weight binaries in second one without renaming.'; + } + console.error(e); + } + } else { + status.innerText = 'Please fill out all the fields with valid values.'; + status.classList.add('error'); + console.error(e); + } + }); +} + function createLayerViewer(id, viewerData, callback, rootCallback) { const layersViewer = new LayersViewer({ id: id, diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index dc62328f5..bfbc5a555 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -315,6 +315,7 @@ function toolsOff() { case 'label': presetLabelOff(); + mlAssistantOff(); break; } } @@ -629,8 +630,10 @@ function mainMenuChange(data) { if (data.labels) { $UI.labelsSideMenu.open(); + $UI.AssistantSideMenu.open(); } else { presetLabelOff(); + mlAsisstantOff(); } } @@ -1789,6 +1792,7 @@ function drawLabel(e) { } else { // off preset label presetLabelOff(); + mlAsisstantOff(); } } @@ -1842,6 +1846,10 @@ function presetLabelOff() { } } +function mlAsisstantOff() { + $UI.AssistantSideMenu.close(); +} + function savePresetLabel() { if ($CAMIC.viewer.canvasDrawInstance._path_index === 0) { // toast diff --git a/apps/viewer/viewer.html b/apps/viewer/viewer.html index 9046410d5..32f9d78dc 100644 --- a/apps/viewer/viewer.html +++ b/apps/viewer/viewer.html @@ -72,6 +72,13 @@ media="all" href="../../components/layersviewer/layersviewer.css" /> + + + + @@ -310,6 +322,11 @@ + + + + + diff --git a/components/ml-assistant/ml-assistant.css b/components/ml-assistant/ml-assistant.css new file mode 100644 index 000000000..99252b070 --- /dev/null +++ b/components/ml-assistant/ml-assistant.css @@ -0,0 +1,403 @@ +.ml-assistant * { + padding:0; + margin:0; + color:#365f9c; +} + +ul.model-tools { + padding: 0; + margin: 0; + height: 2.4rem; + border-radius: 0.5rem; + border: solid #365f9c 0.2rem; + background-color: #365f9c; + width: 94%; + margin: 10px 2%; + display: flex; + justify-content: space-around; +} + +ul.model-tools > li { + cursor: pointer; + height: 100%; + width: 2.4rem; + float: left; + margin-left: 0.2rem; + list-style: none; + color: #365f9c; + background-color: #fff; + /*opacity: 0.5;*/ +} + +ul.model-tools > li > input { + display: none; + /* position: absolute; + font-size: 0; + left: -100%; */ +} + +ul.model-tools > li > input[type="checkbox"]:checked + label { + background-color: #365f9c; + color: #fff; + /*box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.3);*/ +} + +ul.model-tools > li > input[type="checkbox"] + label:hover { + opacity: 1; +} + +/* ------------- */ + +ul.ml_drop_down { + width: 2.4rem; + font-size: 1.4rem; + padding: 0; + margin: 0; + display: none; +} + +ul.model-tools > li:hover ul.ml_drop_down { + flex-direction: column; + display: flex; + width: fit-content; + width: -moz-fit-content; +} + +ul.ml_drop_down li { + cursor: pointer; + /* width: 2.4rem; + height: 2.4rem; */ + /*border-radius:.5rem .5rem .5rem .5rem;*/ + box-shadow: 0 0.4rem 0.8rem 0 rgba(0, 0, 0, 0.2), + 0 0.6rem 0.2rem 0 rgba(0, 0, 0, 0.19); + padding: 3px 0 0 0; + margin: 0; + color: #365f9c; + background-color: #fff; + opacity: 1; + position: relative; + z-index: 100; +} +ul.ml_drop_down li.text { + height: 100%; +} + +ul.ml_drop_down > li > input[type="radio"] { + display: none; + /* position: absolute; + font-size: 0; + left: -100%; */ +} + +li > input[type="radio"] + label div, +li > input[type="radio"] + label { + cursor: pointer; +} +li > input[type="radio"]:checked + label div, +li > input[type="radio"]:checked + label { + cursor: pointer; + color: #fff; + background-color: #365f9c; + /*border-radius:.5rem .5rem .5rem .5rem;*/ + box-shadow: 0 0.4rem 0.8rem 0 rgba(0, 0, 0, 0.2), + 0 0.6rem 0.2rem 0 rgba(0, 0, 0, 0.19); +} +li > input[type="radio"]:checked + label div, +li > input[type="radio"]:checked + label, +li > input[type="radio"] + label div, +li > input[type="radio"] + label { + display: flex; + text-align: center; + align-items: center; + white-space: nowrap; + margin: 0 auto; + /* padding: 0 2px; */ + /* height:2.4rem; */ +} +li > input[type="radio"]:checked + label div, +li > input[type="radio"] + label div { + padding: 0 2px; +} + +.model-tools li { + list-style: none; +} + +li.text > input[type="radio"] + label div { + font-weight: bold; +} + +.ml-assistant > .btn { + padding: 0.2rem 1rem; + width: 96%; + margin: 2%; +} + +.ml-assistant div.annotate-checklist { + box-sizing: border-box; + display: -webkit-flex; + -webkit-justify-content: unset; + -webkit-flex-flow: row wrap; + display: flex; + justify-content: space-around; + flex-flow: row wrap; + padding-bottom: .5rem; + margin: 10px 0; +} + +.ml-assistant div.annotate-checklist > label { + font-size: 1.2rem; + margin: .3rem; +} + +.annotate-setting > div { + display: flex; + width: 100%; + margin: 5px 0; +} + +.annotate-setting > div * { + display: inline-block; +} + +.annotate-setting > div pre { + font-size: 12px; + width: 35%; + display: inherit; + align-items: center; + margin-left: 5px; +} + +.annotate-setting > div input { + height: 15px; + width: 45%; +} + +.annotate-setting > div span { + font-size: 12px; + width: 10%; + display: inherit; + align-items: center; + margin-left: 5px; +} + +.form-style{ + /* color: #464544; */ + color: #57300a; + font-size: 14px; + max-width:400px; + margin:10px auto; + background:#fff; + border-radius:2px; + padding:10px; + /*font-family: Georgia, "Times New Roman", Times, serif;*/ +} +.form-style #mg{ + margin-top: 20px; + text-align: center; +} +.form-style ul{ + list-style:none; + padding:0; + margin:0; +} +.form-style li{ + display: block; + padding: 5px; + border:1px solid #DDDDDD; + margin-bottom: 20px; + border-radius: 3px; +} +.form-style li:last-child{ + border:none; + margin-bottom: 0px; + text-align: center; +} +.form-style li > label{ + display: block; + float: left; + margin-top: -19px; + background: #FFFFFF; + height: 14px; + padding: 2px 5px 2px 5px; + color: #464544; + font-size: 14px; + overflow: hidden; + /*font-family: Arial, Helvetica, sans-serif;*/ +} +.form-style input[type="text"], +.form-style input[type="date"], +.form-style input[type="datetime"], +.form-style input[type="number"], +.form-style input[type="url"], +.form-style textarea, +.form-style select +{ + box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + width: 100%; + display: block; + outline: none; + border: 1px solid black; + height: 25px; + line-height: 25px; + font-size: 16px; + padding: 0; + /*font-family: Georgia, "Times New Roman", Times, serif;*/ +} +.form-style li > span{ + background: #F3F3F3; + display: block; + padding: 3px; + margin: 0 -9px -9px -9px; + text-align: center; + color: rgb(104, 94, 94); + /*font-family: Arial, Helvetica, sans-serif;*/ + font-size: 11px; +} +.form-style textarea{ + resize:none; +} +.form-style input[type="submit"], +.form-style input[type="button"]{ + background: #2147FF; + border: none; + padding: 10px 20px 10px 20px; + border-bottom: 3px solid #5994FF; + border-radius: 3px; + color: #D2E2FF; +} +.form-style input[type="submit"]:hover, +.form-style input[type="button"]:hover{ + background: #6B9FFF; + color:#fff; +} +#upload_panel .modalbox-content{ + width: 30%; +} +.form-style [type=url]:focus, +.form-style input[type=text]:focus, +.form-style input[type=number]:focus +{ + background-color: #ddd; + outline: none; + border:2px; + border-style: solid; +} +.form-style #modelupload, +.form-style #weightsupload{ + cursor: pointer; +} + +.switch { + position: relative; + display: inline-block; + width: 140px; + height: 26px; + border: 1px solid black; +} + +.switch input {display:none;} + + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 18px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: #365F9C; + -webkit-transition: .4s; + transition: .4s; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(106px); + -ms-transform: translateX(26px); + transform: translateX(106px); +} + +.slider:after +{ + content:'Upload'; + display: block; + position: absolute; + transform: translate(-50%,-50%); + top: 50%; + left: 54%; + font-size: 12px; + font-family: Verdana, sans-serif; +} + +input:checked + .slider:after +{ + content:'Link'; + left: 35%; +} + +.checktrue { + display: none; +} +#mtable { + table-layout: fixed; + width: 100%; + border-collapse: collapse; +} +#mtable thead th:nth-child(1) { + width: 15%; +} + +#mtable thead th:nth-child(2) { + width: 30%; +} + +#mtable thead th:nth-child(3) { + width: 15%; +} + +#mtable thead th:nth-child(4) { + width: 15%; +} + +#mtable thead th:nth-child(4) { + width: 25%; +} + +th { + padding: 5px; +} + +td { + padding: 10px; + text-align: center; +} + +thead, tfoot { + background: #d5dbe5; +} + +.btn-del{ + background-color: red; + border: none; + color: white; + padding: 12px 16px; + font-size: 14px; + cursor: pointer; +} diff --git a/components/ml-assistant/ml-assistant.js b/components/ml-assistant/ml-assistant.js new file mode 100644 index 000000000..feb04fc49 --- /dev/null +++ b/components/ml-assistant/ml-assistant.js @@ -0,0 +1,347 @@ +// Proposal: Control panel for machine learning assistant +// Include: +// + Add model -> Open add model modal +// + Model enable +// + Model selection (Options: watershed, smartpen, ...) +// + Pixel scaling selection (4 options: No Scaling, Normalization, Centerization, Standardization) +// + Model info -> Open model info modal + +// + Annotate Mode zone (checkbox) +// - Draw +// - Click +// - ROI + +// + Setting Zone (input range) +// - Radius +// - Threshold +// - Roughness + + +function Assistant(options) { + this.className = 'Assistant'; + this.setting = { + // id: doc element + // data: labels dataset + }; + this.view; + this.addBtn; + this.enableBtn; + this.modelSelectionBtn; + this.infoBtn; + + this.annotateModeZone; + this.settingZone; + + extend(this.setting, options); + this.elt = document.getElementById(this.setting.id); + if (!this.elt) { + console.error(`${this.className}: No Main Elements...`); + return; + } + + this.elt.classList.add('ml-assistant-container'); + + this.__refreshUI(); +} +Assistant.prototype.__clearUI = function() { + empty(this.elt); + + this.view = null; + this.addBtn = null; + this.enableBtn = null; + this.modelSelectionBtn = null; + this.infoBtn = null; + this.modelList = null; + this.pixelScaleList = null; + + this.annotateModeZone = null; + this.settingZone = null; +}; + +Assistant.prototype.__refreshUI = async function() { + this.__clearUI(); + // + this._viewer = this.setting.viewer; + this.view = document.createElement('div'); + this.view.classList.add('ml-assistant'); + const modelEnableId = randomId(); + const modelSelectionId = randomId(); + const pixelScalingId = randomId(); + const modelInfoId = randomId(); + + // Pixel Scaling ID + const noScaleId = randomId(); + const normId = randomId(); + const centerId = randomId(); + const stdId = randomId(); + + const viewHtml = ` + +
    +
  • + + +
  • +
  • + + +
      +
    +
  • +
  • + + +
      +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    • +
    +
  • +
  • + info +
  • +
+
+ + + +
+
+
+
Radius
+ + 30 +
+
+
Threshold
+ + 90 +
+
+
Roughness
+ + 4 +
+
+ `; + + this.view.innerHTML = viewHtml; + this.elt.append(this.view); + + this.addBtn = this.view.querySelector('.add-model'); + this.enableBtn = this.view.querySelector(`#${modelEnableId}`); + this.modelSelectionBtn = this.view.querySelector(`#${modelSelectionId}`); + this.infoBtn = this.view.querySelector(`#${modelInfoId}`); + this.modelList = this.view.querySelector('.model-list'); + this.pixelScaleList = this.view.querySelector('.pixel-scale-list') + + this.annotateModeZone = this.view.querySelector('.annotate-checklist'); + this.settingZone = { + radius: this.view.querySelector('.radius-setting'), + threshold: this.view.querySelector('.threshold-setting'), + roughness: this.view.querySelector('.roughness-setting'), + } + + await this.__createModelList(); + + this.__assignEventListener(); +} + +Assistant.prototype.__assignEventListener = function() { + // Open add model modal event + this.addBtn.addEventListener('click', (event) => { + // event.preventDefault(); + this.__openAddModel(); + }) + + // Enable Annotation Event + // TODO + + // Switch Current Model Event + this.modelList.querySelectorAll('label').forEach((elt) => { + elt.addEventListener('click', (event) => { + this.modelList.querySelectorAll('input').forEach((iElt) => { + iElt.checked = false; + }); + event.target.checked = true; + }) + }) + + // Switch Pixel Scaling Event + this.pixelScaleList.querySelectorAll('label').forEach((elt) => { + elt.addEventListener('click', (event) => { + this.pixelScaleList.querySelectorAll('input').forEach((iElt) => { + iElt.checked = false; + }); + event.target.checked = true; + }) + }) + + + // Open Model Info Event + this.infoBtn.addEventListener('click', (event) => { + // event.preventDefault(); + this.__openModelInfo(); + }) + + // Switch Model Mode Event + this.annotateModeZone.querySelectorAll('input').forEach((elt) => { + elt.addEventListener('click', (event) => { + this.annotateModeZone.querySelectorAll('input').forEach((iElt) => { + iElt.checked = false; + }) + event.target.checked = true; + }) + }) + + // Change radius event + this.settingZone.radius.querySelector('input').addEventListener('change', (event) => { + this.settingZone.radius.querySelector('span').textContent = event.target.value; + // TODO process ROI processing + }) + + // Change threshold event + this.settingZone.threshold.querySelector('input').addEventListener('change', (event) => { + this.settingZone.threshold.querySelector('span').textContent = event.target.value; + // TODO process ROI processing + }) + + // Change roughness event + this.settingZone.roughness.querySelector('input').addEventListener('change', (event) => { + this.settingZone.roughness.querySelector('span').textContent = event.target.value; + // TODO process ROI processing + }) +} + +// Add model list +Assistant.prototype.__createModelList = async function() { + const dropDownList = [ + { + icon: 'timeline', + title: 'Watershed', + value: 'watershed', + checked: true, + }]; + + // modelName = []; + Object.keys(await tf.io.listModels()).forEach(function(element) { + const dict = {}; + const value = element.split('/').pop(); + if (value.slice(0, 3) == 'seg') { + const title = element.split('/').pop().split('_')[1].slice(0, -3); + dict.icon = 'flip_to_back'; + dict.title = title; + dict.value = value; + dict.checked = false; + // Saving to previously defined model names + // modelName.push(dict['title']); + dropDownList.push(dict); + } + }); + + dropDownList.map((modelData) => { + this.__createModelLabel(modelData); + }) +} + +// Create model label +Assistant.prototype.__createModelLabel = function(modelData) { + const modelId = randomId(); + const value = modelData.value; + const title = modelData.title; + const checked = modelData.checked; + const icon = modelData.icon; + const modelCardHtml = ` + + + ` + const modelCard = document.createElement('li'); + modelCard.innerHTML = modelCardHtml; + if (checked) { + modelCard.querySelector('input').checked = true; + } + this.modelList.appendChild(modelCard); +} + +// Handle open model info modal +Assistant.prototype.__openAddModel = function() { + //Raise open add model modal into view + this._viewer.raiseEvent('open-add-model', {}); +} + +// Add model +Assistant.prototype.__updateModelList= function() { + empty(this.modelList); + this.__createModelList(); +} + +// Handle enable machine learning assistant +Assistant.prototype.__enableAssistant = function() { + // Change UI + this._viewer.raiseEvent('enable-assistant', {}); +} + +// Handle model selection +Assistant.prototype.__selectModel = function(modelValue) { + // Change UI + this._viewer.raiseEvent('select-model', {model: modelValue}); +} + +// Handle open model info modal +Assistant.prototype.__openModelInfo = function() { + this._viewer.raiseEvent('open-model-info', {}); +} + +// Handle model delete +Assistant.prototype.__deleteModel = function(modelValue) { + // Delete model from resource + + // Remove UI + this.__updateModelList(); +} + +// Handle select mode +Assistant.prototype.__selectMode = function(mode) { + // Change UI +} + +// Get mode +Assistant.prototype.__getAssistantMode = function() { + const mode = {}; + return mode; +} + +// Handle setting mode change +Assistant.prototype.__settingModeChangeHandler = function() { + // Change UI +} + +// Get setting mode value +Assistant.prototype.__getSettingModes = function() { + const settingMode = { + radius: this.settingZone.radius.value, + threshold: this.settingZone.threshold.value, + roughness: this.settingZone.threshold.value, + } + return settingMode; +} + +Assistant.prototype.__createElementFromHTML= function(htmlString) { + var div = document.createElement('div'); + div.innerHTML = htmlString.trim(); + // Change this to div.childNodes to support multiple top-level nodes + return div.firstChild; +}; diff --git a/components/sidemenu/sidemenu.css b/components/sidemenu/sidemenu.css index c37e85dea..46e45bc54 100644 --- a/components/sidemenu/sidemenu.css +++ b/components/sidemenu/sidemenu.css @@ -1,6 +1,6 @@ .side_menu { - height: 100vh; + /* height: 100vh; */ width: 0; position: fixed; z-index: 1; @@ -9,7 +9,6 @@ border:#cbcbcb 1px solid; box-shadow: 1px 2px 3px #aaa; background-color: rgba(245,245,245,1); - border-right:; overflow-x: hidden; transition: 0.5s; } @@ -37,10 +36,19 @@ float: right; color:#365f9c; } + +.side_menu > div.close > i.right { + float: left; +} + .side_menu > div.close > i.sec { margin-right:-1.9rem; } +.side_menu > div.close > i.fir { + margin-right:-1.9rem; +} + .side_content{ flex: 1; diff --git a/components/sidemenu/sidemenu.js b/components/sidemenu/sidemenu.js index db4ef3965..38e9253a7 100644 --- a/components/sidemenu/sidemenu.js +++ b/components/sidemenu/sidemenu.js @@ -84,7 +84,24 @@ SideMenu.prototype.__refresh = function() { empty(this.elt); this.elt.style.width = 0; - this.elt.style.left = `-10px`; + + if (this.setting.height) { + this.elt.style.height = this.setting.height; + } else { + this.elt.style.height = '100vh'; + } + + if (this.setting.top) { + this.elt.style.top = this.setting.top; + } else { + this.elt.style.top = '0'; + } + + if (this.setting.position === 'right') { + this.elt.style.right = `-10px`; + } else { + this.elt.style.left = `-10px`; + } this.elt.classList.add('side_menu'); this.elt.classList.add('flex-container'); @@ -92,16 +109,31 @@ SideMenu.prototype.__refresh = function() { this._close_handler = document.createElement('div'); this._close_handler.classList.add('close'); - const icon1 = document.createElement('i'); - icon1.classList.add('material-icons'); - icon1.classList.add('md-24'); - icon1.textContent = 'chevron_left'; - - const icon2 = icon1.cloneNode(true); - icon2.classList.add('sec'); + if (this.setting.position === 'right') { + const icon1 = document.createElement('i'); + icon1.classList.add('material-icons'); + icon1.classList.add('md-24'); + icon1.classList.add('right'); + icon1.textContent = 'chevron_right'; + + const icon2 = icon1.cloneNode(true); + + icon1.classList.add('fir'); + this._close_handler.appendChild(icon1); + this._close_handler.appendChild(icon2); + } else { + const icon1 = document.createElement('i'); + icon1.classList.add('material-icons'); + icon1.classList.add('md-24'); + icon1.textContent = 'chevron_left'; + + const icon2 = icon1.cloneNode(true); + icon2.classList.add('sec'); + + this._close_handler.appendChild(icon1); + this._close_handler.appendChild(icon2); + } - this._close_handler.appendChild(icon1); - this._close_handler.appendChild(icon2); this.elt.appendChild(this._close_handler); // create side panel content panel @@ -115,8 +147,13 @@ SideMenu.prototype.__refresh = function() { * open the side menu */ SideMenu.prototype.open = function() { - this.elt.style.left = 0; this.elt.style.width = this.setting.width+'px'; + if (this.setting.position === 'right') { + this.elt.style.left = 'unset'; + this.elt.style.right = 0; + } else { + this.elt.style.left = 0; + } if (this.setting.callback) { this.setting.callback.call(this, { @@ -129,7 +166,11 @@ SideMenu.prototype.open = function() { * close the side menu */ SideMenu.prototype.close = function() { - this.elt.style.left = `-10px`; + if (this.setting.position === 'right') { + this.elt.style.right = `-10px`; + } else { + this.elt.style.left = `-10px`; + } this.elt.style.width = 0; if (this.setting.callback) { this.setting.callback.call(this, { From d06742f05b853a60a9bd3beb73b44c77000a8e81 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Mon, 26 Jun 2023 23:35:10 -0400 Subject: [PATCH 06/46] don't want submodules here --- Caracal | 1 - Distro | 1 - 2 files changed, 2 deletions(-) delete mode 160000 Caracal delete mode 160000 Distro diff --git a/Caracal b/Caracal deleted file mode 160000 index 945e448e8..000000000 --- a/Caracal +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 945e448e8e03ac72c26a7b4eee2b6f2b41f7d725 diff --git a/Distro b/Distro deleted file mode 160000 index 4f107ac10..000000000 --- a/Distro +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4f107ac106b8ebd0422de32b6e9d69d00d0f517d From 069cb9ef5190026159880f3238f474151cf99441 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Thu, 29 Jun 2023 15:24:18 -0400 Subject: [PATCH 07/46] wait for pathdb code in redir.js --- apps/redir.js | 60 ++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/apps/redir.js b/apps/redir.js index 968dd7656..732614b22 100644 --- a/apps/redir.js +++ b/apps/redir.js @@ -1,31 +1,33 @@ -let params = getUrlVars(); -let isPathdb = false; - -if (params.hasOwnProperty('mode') && params.mode == 'pathdb') { - PathDbMods(); - isPathdb = true; -} - -const store = new Store('../data/'); - -if (params.hasOwnProperty('slide')) { - store.findSlide(params.slide, params.specimen, params.study, 0, 0, params.collection).then((x)=>{ - console.info(x); - if (x.length <=0) { - throw new Error('No Matches found'); - } - if (isPathdb) { - // get the pathdb url - window.location = './viewer/viewer.html?slideId=' + x[0]['_id']['$oid'] + '&mode=pathdb'; - } else { - // get the normal url - window.location = './viewer/viewer.html?slideId=' + x[0]['_id']['$oid']; - } - }).catch((e)=>{ - console.error(e); +document.addEventListener('DOMContentLoaded', async function() { + let params = getUrlVars(); + let isPathdb = false; + + if (params.hasOwnProperty('mode') && params.mode == 'pathdb') { + await PathDbMods(); + isPathdb = true; + } + + const store = new Store('../data/'); + + if (params.hasOwnProperty('slide')) { + store.findSlide(params.slide, params.specimen, params.study, 0, 0, params.collection).then((x)=>{ + console.info(x); + if (x.length <=0) { + throw new Error('No Matches found'); + } + if (isPathdb) { + // get the pathdb url + window.location = './viewer/viewer.html?slideId=' + x[0]['_id']['$oid'] + '&mode=pathdb'; + } else { + // get the normal url + window.location = './viewer/viewer.html?slideId=' + x[0]['_id']['$oid']; + } + }).catch((e)=>{ + console.error(e); + alert('ERROR!'); + }); + } else { + console.error('no slide passed?'); alert('ERROR!'); - }); -} else { - console.error('no slide passed?'); - alert('ERROR!'); + } } From 2c928ef6da67d0d5860889b5686a7947f5b97115 Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Thu, 29 Jun 2023 15:36:57 -0400 Subject: [PATCH 08/46] add ending paren --- apps/redir.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/redir.js b/apps/redir.js index 732614b22..5cdeba688 100644 --- a/apps/redir.js +++ b/apps/redir.js @@ -30,4 +30,4 @@ document.addEventListener('DOMContentLoaded', async function() { console.error('no slide passed?'); alert('ERROR!'); } -} +}) From 72affc7f2eea90b7172623818bbca239a2cfc9e8 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Sat, 8 Jul 2023 16:23:22 +0700 Subject: [PATCH 09/46] Add machine learning assistant draw mode --- apps/segment/segment.js | 3 + apps/viewer/init.js | 1 + apps/viewer/turf.min.js | 88 +++ apps/viewer/uicallbacks.js | 12 +- apps/viewer/viewer.html | 10 + components/ml-assistant/ml-assistant.js | 5 +- components/ml-assistant/ml-tool.js | 519 ++++++++++++++++++ .../openseadragon-canvas-draw-overlay.js | 31 +- 8 files changed, 663 insertions(+), 6 deletions(-) create mode 100644 apps/viewer/turf.min.js create mode 100644 components/ml-assistant/ml-tool.js diff --git a/apps/segment/segment.js b/apps/segment/segment.js index e164cbb06..a99410095 100644 --- a/apps/segment/segment.js +++ b/apps/segment/segment.js @@ -883,9 +883,11 @@ async function segmentModel(key) { // dummy.getContext('2d').drawImage(img, dx, dy);b const imgData = fullResCvs.getContext('2d').getImageData(0, 0, fullResCvs.width, fullResCvs.height); + console.log('imgData: ', imgData); let val; tf.tidy(()=>{ const img = tf.browser.fromPixels(imgData).toFloat(); + console.log('img: ', img); let img2; if (inputChannels == 1) { img2 = tf.image.resizeBilinear(img, [imageSize, imageSize]).mean(2); @@ -918,6 +920,7 @@ async function segmentModel(key) { normalized = img2.sub(mean).div(std); } const batched = normalized.reshape([1, imageSize, imageSize, inputChannels]); + console.log('batched: ', batched); let values = model.predict(batched).dataSync(); values = Array.from(values); // scale values diff --git a/apps/viewer/init.js b/apps/viewer/init.js index 6536e6824..cfe251ed9 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -58,6 +58,7 @@ window.addEventListener('keydown', (e) => { measurementOff(); annotationOff(); presetLabelOff(); + mlAsisstantOff(); } // open annotation (ctrl + a) diff --git a/apps/viewer/turf.min.js b/apps/viewer/turf.min.js new file mode 100644 index 000000000..17021fd74 --- /dev/null +++ b/apps/viewer/turf.min.js @@ -0,0 +1,88 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).turf={})}(this,(function(t){"use strict";var e=6371008.8,n={centimeters:637100880,centimetres:637100880,degrees:57.22891354143274,feet:20902260.511392,inches:39.37*e,kilometers:6371.0088,kilometres:6371.0088,meters:e,metres:e,miles:3958.761333810546,millimeters:6371008800,millimetres:6371008800,nauticalmiles:e/1852,radians:1,yards:6967335.223679999},r={centimeters:100,centimetres:100,degrees:1/111325,feet:3.28084,inches:39.37,kilometers:.001,kilometres:.001,meters:1,metres:1,miles:1/1609.344,millimeters:1e3,millimetres:1e3,nauticalmiles:1/1852,radians:1/e,yards:1.0936133},i={acres:247105e-9,centimeters:1e4,centimetres:1e4,feet:10.763910417,hectares:1e-4,inches:1550.003100006,kilometers:1e-6,kilometres:1e-6,meters:1,metres:1,miles:386e-9,millimeters:1e6,millimetres:1e6,yards:1.195990046};function o(t,e,n){void 0===n&&(n={});var r={type:"Feature"};return(0===n.id||n.id)&&(r.id=n.id),n.bbox&&(r.bbox=n.bbox),r.properties=e||{},r.geometry=t,r}function s(t,e,n){switch(t){case"Point":return a(e).geometry;case"LineString":return h(e).geometry;case"Polygon":return l(e).geometry;case"MultiPoint":return d(e).geometry;case"MultiLineString":return g(e).geometry;case"MultiPolygon":return y(e).geometry;default:throw new Error(t+" is invalid")}}function a(t,e,n){if(void 0===n&&(n={}),!t)throw new Error("coordinates is required");if(!Array.isArray(t))throw new Error("coordinates must be an Array");if(t.length<2)throw new Error("coordinates must be at least 2 numbers long");if(!C(t[0])||!C(t[1]))throw new Error("coordinates must contain numbers");return o({type:"Point",coordinates:t},e,n)}function u(t,e,n){return void 0===n&&(n={}),f(t.map((function(t){return a(t,e)})),n)}function l(t,e,n){void 0===n&&(n={});for(var r=0,i=t;r=0))throw new Error("precision must be a positive number");var n=Math.pow(10,e||0);return Math.round(t*n)/n}function m(t,e){void 0===e&&(e="kilometers");var r=n[e];if(!r)throw new Error(e+" units is invalid");return t*r}function x(t,e){void 0===e&&(e="kilometers");var r=n[e];if(!r)throw new Error(e+" units is invalid");return t/r}function E(t,e){return w(x(t,e))}function b(t){var e=t%360;return e<0&&(e+=360),e}function w(t){return 180*(t%(2*Math.PI))/Math.PI}function I(t){return t%360*Math.PI/180}function N(t,e,n){if(void 0===e&&(e="kilometers"),void 0===n&&(n="kilometers"),!(t>=0))throw new Error("length must be a positive number");return m(x(t,e),n)}function S(t,e,n){if(void 0===e&&(e="meters"),void 0===n&&(n="kilometers"),!(t>=0))throw new Error("area must be a positive number");var r=i[e];if(!r)throw new Error("invalid original units");var o=i[n];if(!o)throw new Error("invalid final units");return t/r*o}function C(t){return!isNaN(t)&&null!==t&&!Array.isArray(t)}function P(t){return!!t&&t.constructor===Object}function M(t){if(!t)throw new Error("bbox is required");if(!Array.isArray(t))throw new Error("bbox must be an Array");if(4!==t.length&&6!==t.length)throw new Error("bbox must be an Array of 4 or 6 numbers");t.forEach((function(t){if(!C(t))throw new Error("bbox must only contain numbers")}))}function L(t){if(!t)throw new Error("id is required");if(-1===["string","number"].indexOf(typeof t))throw new Error("id must be a number or a string")}var O=Object.freeze({__proto__:null,earthRadius:e,factors:n,unitsFactors:r,areaFactors:i,feature:o,geometry:s,point:a,points:u,polygon:l,polygons:c,lineString:h,lineStrings:p,featureCollection:f,multiLineString:g,multiPoint:d,multiPolygon:y,geometryCollection:v,round:_,radiansToLength:m,lengthToRadians:x,lengthToDegrees:E,bearingToAzimuth:b,radiansToDegrees:w,degreesToRadians:I,convertLength:N,convertArea:S,isNumber:C,isObject:P,validateBBox:M,validateId:L});function R(t,e,n){if(null!==t)for(var r,i,o,s,a,u,l,c,h=0,p=0,f=t.type,g="FeatureCollection"===f,d="Feature"===f,y=g?t.features.length:1,v=0;va||f>u||g>l)return s=o,a=n,u=f,l=g,void(i=0);var d=h([s,o],t.properties);if(!1===e(d,n,r,g,i))return!1;i++,s=o}))&&void 0}}}))}function V(t,e,n){var r=n,i=!1;return U(t,(function(t,o,s,a,u){r=!1===i&&void 0===n?t:e(r,t,o,s,a,u),i=!0})),r}function X(t,e){if(!t)throw new Error("geojson is required");z(t,(function(t,n,r){if(null!==t.geometry){var i=t.geometry.type,o=t.geometry.coordinates;switch(i){case"LineString":if(!1===e(t,n,r,0,0))return!1;break;case"Polygon":for(var s=0;st[0]&&(e[0]=t[0]),e[1]>t[1]&&(e[1]=t[1]),e[2]=2&&!Array.isArray(t[0])&&!Array.isArray(t[1]))return t;throw new Error("coord must be GeoJSON Point or an Array of numbers")}function Q(t){if(Array.isArray(t))return t;if("Feature"===t.type){if(null!==t.geometry)return t.geometry.coordinates}else if(t.coordinates)return t.coordinates;throw new Error("coords must be GeoJSON Feature, Geometry Object or an Array")}function $(t){if(t.length>1&&C(t[0])&&C(t[1]))return!0;if(Array.isArray(t[0])&&t[0].length)return $(t[0]);throw new Error("coordinates must only contain numbers")}function tt(t,e,n){if(!e||!n)throw new Error("type and name required");if(!t||t.type!==e)throw new Error("Invalid input to "+n+": must be a "+e+", given "+t.type)}function et(t,e,n){if(!t)throw new Error("No feature passed");if(!n)throw new Error(".featureOf() requires a name");if(!t||"Feature"!==t.type||!t.geometry)throw new Error("Invalid input to "+n+", Feature with geometry required");if(!t.geometry||t.geometry.type!==e)throw new Error("Invalid input to "+n+": must be a "+e+", given "+t.geometry.type)}function nt(t,e,n){if(!t)throw new Error("No featureCollection passed");if(!n)throw new Error(".collectionOf() requires a name");if(!t||"FeatureCollection"!==t.type)throw new Error("Invalid input to "+n+", FeatureCollection required");for(var r=0,i=t.features;r + * v. 1.2.0 + * https://github.com/RaumZeit/MarchingSquares.js + * + * MarchingSquaresJS is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MarchingSquaresJS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * As additional permission under GNU Affero General Public License version 3 + * section 7, third-party projects (personal or commercial) may distribute, + * include, or link against UNMODIFIED VERSIONS of MarchingSquaresJS without the + * requirement that said third-party project for that reason alone becomes + * subject to any requirement of the GNU Affero General Public License version 3. + * Any modifications to MarchingSquaresJS, however, must be shared with the public + * and made available. + * + * In summary this: + * - allows you to use MarchingSquaresJS at no cost + * - allows you to use MarchingSquaresJS for both personal and commercial purposes + * - allows you to distribute UNMODIFIED VERSIONS of MarchingSquaresJS under any + * license as long as this license notice is included + * - enables you to keep the source code of your program that uses MarchingSquaresJS + * undisclosed + * - forces you to share any modifications you have made to MarchingSquaresJS, + * e.g. bug-fixes + * + * You should have received a copy of the GNU Affero General Public License + * along with MarchingSquaresJS. If not, see . + */function ft(t,e,n){n=n||{};for(var r=Object.keys(ht),i=0;i=0&&y>=0&&y=0;h--)if(Math.abs(e[h][0][0]-l)<=r&&Math.abs(e[h][0][1]-c)<=r){for(var p=a.path.length-2;p>=0;--p)e[h].unshift(a.path[p]);u=!0;break}u||(e[n++]=a.path)}var f}))})),e}(function(t,e){for(var n=t.length-1,r=t[0].length-1,i={rows:n,cols:r,cells:[]},o=0;o=e?8:0,a|=l>=e?4:0,a|=c>=e?2:0;var p,f,g,d,y=!1;if(5===(a|=h>=e?1:0)||10===a){var v=(u+l+c+h)/4;5===a&&vn;){if(r-n>600){var o=r-n+1,s=e-n+1,a=Math.log(o),u=.5*Math.exp(2*a/3),l=.5*Math.sqrt(a*u*(o-u)/o)*(s-o/2<0?-1:1);bt(t,e,Math.max(n,Math.floor(e-s*u/o+l)),Math.min(r,Math.floor(e+(o-s)*u/o+l)),i)}var c=t[e],h=n,p=r;for(wt(t,n,e),i(t[r],c)>0&&wt(t,n,r);h0;)p--}0===i(t[n],c)?wt(t,n,p):wt(t,++p,r),p<=e&&(n=p+1),e<=p&&(r=p-1)}}function wt(t,e,n){var r=t[e];t[e]=t[n],t[n]=r}function It(t,e){return te?1:0}mt.default=xt;var Nt=Ct,St=Ct;function Ct(t,e){if(!(this instanceof Ct))return new Ct(t,e);this._maxEntries=Math.max(4,t||9),this._minEntries=Math.max(2,Math.ceil(.4*this._maxEntries)),e&&this._initFormat(e),this.clear()}function Pt(t,e,n){if(!n)return e.indexOf(t);for(var r=0;r=t.minX&&e.maxY>=t.minY}function Gt(t){return{children:t,height:1,leaf:!0,minX:1/0,minY:1/0,maxX:-1/0,maxY:-1/0}}function qt(t,e,n,r,i){for(var o,s=[e,n];s.length;)(n=s.pop())-(e=s.pop())<=r||(o=e+Math.ceil((n-e)/r/2)*r,mt(t,o,e,n,i),s.push(e,o,o,n))}function Bt(t){var e={exports:{}};return t(e,e.exports),e.exports}Ct.prototype={all:function(){return this._all(this.data,[])},search:function(t){var e=this.data,n=[],r=this.toBBox;if(!kt(t,e))return n;for(var i,o,s,a,u=[];e;){for(i=0,o=e.children.length;i=0&&o[e].children.length>this._maxEntries;)this._split(o,e),e--;this._adjustParentBBoxes(i,o,e)},_split:function(t,e){var n=t[e],r=n.children.length,i=this._minEntries;this._chooseSplitAxis(n,i,r);var o=this._chooseSplitIndex(n,i,r),s=Gt(n.children.splice(o,n.children.length-o));s.height=n.height,s.leaf=n.leaf,Mt(n,this.toBBox),Mt(s,this.toBBox),e?t[e-1].children.push(s):this._splitRoot(n,s)},_splitRoot:function(t,e){this.data=Gt([t,e]),this.data.height=t.height+1,this.data.leaf=!1,Mt(this.data,this.toBBox)},_chooseSplitIndex:function(t,e,n){var r,i,o,s,a,u,l,c,h,p,f,g,d,y;for(u=l=1/0,r=e;r<=n-e;r++)i=Lt(t,0,r,this.toBBox),o=Lt(t,r,n,this.toBBox),h=i,p=o,f=void 0,g=void 0,d=void 0,y=void 0,f=Math.max(h.minX,p.minX),g=Math.max(h.minY,p.minY),d=Math.min(h.maxX,p.maxX),y=Math.min(h.maxY,p.maxY),s=Math.max(0,d-f)*Math.max(0,y-g),a=At(i)+At(o),s=e;i--)o=t.children[i],Ot(u,t.leaf?s(o):o),l+=Dt(u);return l},_adjustParentBBoxes:function(t,e,n){for(var r=n;r>=0;r--)Ot(e[r],t)},_condense:function(t){for(var e,n=t.length-1;n>=0;n--)0===t[n].children.length?n>0?(e=t[n-1].children).splice(e.indexOf(t[n]),1):this.clear():Mt(t[n],this.toBBox)},_initFormat:function(t){var e=["return a"," - b",";"];this.compareMinX=new Function("a","b",e.join(t[0])),this.compareMinY=new Function("a","b",e.join(t[1])),this.toBBox=new Function("a","return {minX: a"+t[0]+", minY: a"+t[1]+", maxX: a"+t[2]+", maxY: a"+t[3]+"};")}},Nt.default=St;var zt=function(t,e,n){var r=t*e,i=jt*t,o=i-(i-t),s=t-o,a=jt*e,u=a-(a-e),l=e-u,c=s*l-(r-o*u-s*u-o*l);if(n)return n[0]=c,n[1]=r,n;return[c,r]},jt=+(Math.pow(2,27)+1);var Ut=function(t,e){var n=0|t.length,r=0|e.length;if(1===n&&1===r)return function(t,e){var n=t+e,r=n-t,i=t-(n-r)+(e-r);if(i)return[i,n];return[n]}(t[0],e[0]);var i,o,s=new Array(n+r),a=0,u=0,l=0,c=Math.abs,h=t[u],p=c(h),f=e[l],g=c(f);p=r?(i=h,(u+=1)=r?(i=h,(u+=1)>1;return["sum(",n(t.slice(0,e)),",",n(t.slice(e)),")"].join("")}function r(t){if(2===t.length)return[["sum(prod(",t[0][0],",",t[1][1],"),prod(-",t[0][1],",",t[1][0],"))"].join("")];for(var i=[],o=0;o0){if(s<=0)return a;r=i+s}else{if(!(i<0))return a;if(s>=0)return a;r=-(i+s)}var u=33306690738754716e-32*r;return a>=u||a<=-u?a:o(t,e,n)},function(t,e,n,r){var i=t[0]-r[0],o=e[0]-r[0],a=n[0]-r[0],u=t[1]-r[1],l=e[1]-r[1],c=n[1]-r[1],h=t[2]-r[2],p=e[2]-r[2],f=n[2]-r[2],g=o*c,d=a*l,y=a*u,v=i*c,_=i*l,m=o*u,x=h*(g-d)+p*(y-v)+f*(_-m),E=7771561172376103e-31*((Math.abs(g)+Math.abs(d))*Math.abs(h)+(Math.abs(y)+Math.abs(v))*Math.abs(p)+(Math.abs(_)+Math.abs(m))*Math.abs(f));return x>E||-x>E?x:s(t,e,n,r)}];function u(t){var e=a[t.length];return e||(e=a[t.length]=i(t.length)),e.apply(void 0,t)}!function(){for(;a.length<=5;)a.push(i(a.length));for(var e=[],n=["slow"],r=0;r<=5;++r)e.push("a"+r),n.push("o"+r);var o=["function getOrientation(",e.join(),"){switch(arguments.length){case 0:case 1:return 0;"];for(r=2;r<=5;++r)o.push("case ",r,":return o",r,"(",e.slice(0,r).join(),");");o.push("}var s=new Array(arguments.length);for(var i=0;i1&&Jt(t[o[l-2]],t[o[l-1]],u)<=0;)l-=1,o.pop();for(o.push(a),l=s.length;l>1&&Jt(t[s[l-2]],t[s[l-1]],u)>=0;)l-=1,s.pop();s.push(a)}n=new Array(s.length+o.length-2);for(var c=0,h=(r=0,o.length);r0;--p)n[c++]=s[p];return n},Jt=Ht[3];var Zt=Qt,Kt=Qt;function Qt(t,e){if(!(this instanceof Qt))return new Qt(t,e);if(this.data=t||[],this.length=this.data.length,this.compare=e||$t,this.length>0)for(var n=(this.length>>1)-1;n>=0;n--)this._down(n)}function $t(t,e){return te?1:0}Qt.prototype={push:function(t){this.data.push(t),this.length++,this._up(this.length-1)},pop:function(){if(0!==this.length){var t=this.data[0];return this.length--,this.length>0&&(this.data[0]=this.data[this.length],this._down(0)),this.data.pop(),t}},peek:function(){return this.data[0]},_up:function(t){for(var e=this.data,n=this.compare,r=e[t];t>0;){var i=t-1>>1,o=e[i];if(n(r,o)>=0)break;e[t]=o,t=i}e[t]=r},_down:function(t){for(var e=this.data,n=this.compare,r=this.length>>1,i=e[t];t=0)break;e[t]=a,t=o}e[t]=i}},Zt.default=Kt;var te=function(t,e){for(var n=t[0],r=t[1],i=!1,o=0,s=e.length-1;or!=c>r&&n<(l-a)*(r-u)/(c-u)+a&&(i=!i)}return i},ee=Ht[3],ne=ie,re=ie;function ie(t,e,n){e=Math.max(0,void 0===e?2:e),n=n||0;for(var r,i=function(t){for(var e=t[0],n=t[0],r=t[0],i=t[0],o=0;or[0]&&(r=s),s[1]i[1]&&(i=s)}var a=[e,n,r,i],u=a.slice();for(o=0;oo||a.push({node:c,dist:h})}for(;a.length&&!a.peek().node.children;){var p=a.pop(),f=p.node,g=fe(f,e,n),d=fe(f,r,i);if(p.dist=e.minX&&t[0]<=e.maxX&&t[1]>=e.minY&&t[1]<=e.maxY}function le(t,e,n){for(var r,i,o,s,a=Math.min(t[0],e[0]),u=Math.min(t[1],e[1]),l=Math.max(t[0],e[0]),c=Math.max(t[1],e[1]),h=n.search({minX:a,minY:u,maxX:l,maxY:c}),p=0;p0!=ee(r,i,s)>0&&ee(o,s,r)>0!=ee(o,s,i)>0)return!1;return!0}function ce(t){var e=t.p,n=t.next.p;return t.minX=Math.min(e[0],n[0]),t.minY=Math.min(e[1],n[1]),t.maxX=Math.max(e[0],n[0]),t.maxY=Math.max(e[1],n[1]),t}function he(t,e){var n={p:t,prev:null,next:null,minX:0,minY:0,maxX:0,maxY:0};return e?(n.next=e.next,n.prev=e,e.next.prev=n,e.next=n):(n.prev=n,n.next=n),n}function pe(t,e){var n=t[0]-e[0],r=t[1]-e[1];return n*n+r*r}function fe(t,e,n){var r=e[0],i=e[1],o=n[0]-r,s=n[1]-i;if(0!==o||0!==s){var a=((t[0]-r)*o+(t[1]-i)*s)/(o*o+s*s);a>1?(r=n[0],i=n[1]):a>0&&(r+=o*a,i+=s*a)}return(o=t[0]-r)*o+(s=t[1]-i)*s}function ge(t,e,n,r,i,o,s,a){var u,l,c,h,p=n-t,f=r-e,g=s-i,d=a-o,y=t-i,v=e-o,_=p*p+f*f,m=p*g+f*d,x=g*g+d*d,E=p*y+f*v,b=g*y+d*v,w=_*x-m*m,I=w,N=w;0===w?(l=0,I=1,h=b,N=x):(h=_*b-m*E,(l=m*b-x*E)<0?(l=0,h=b,N=x):l>I&&(l=I,h=b+m,N=x)),h<0?(h=0,-E<0?l=0:-E>_?l=I:(l=-E,I=_)):h>N&&(h=N,-E+m<0?l=0:-E+m>_?l=I:(l=-E+m,I=_));var S=(1-(c=0===h?0:h/N))*i+c*s-((1-(u=0===l?0:l/I))*t+u*n),C=(1-c)*o+c*a-((1-u)*e+u*r);return S*S+C*C}function de(t,e){void 0===e&&(e={}),e.concavity=e.concavity||1/0;var n=[];if(R(t,(function(t){n.push([t[0],t[1]])})),!n.length)return null;var r=ne(n,e.concavity);return r.length>3?l([r]):null}function ye(t,e,n){if(void 0===n&&(n={}),!t)throw new Error("point is required");if(!e)throw new Error("polygon is required");var r=K(t),i=rt(e),o=i.type,s=e.bbox,a=i.coordinates;if(s&&!1===function(t,e){return e[0]<=t[0]&&e[1]<=t[1]&&e[2]>=t[0]&&e[3]>=t[1]}(r,s))return!1;"Polygon"===o&&(a=[a]);for(var u=!1,l=0;lt[1]!=l>t[1]&&t[0]<(u-s)*(t[1]-a)/(l-a)+s&&(r=!r)}return r}function _e(t,e){var n=[];return F(t,(function(t){var r=!1;if("Point"===t.geometry.type)q(e,(function(e){ye(t,e)&&(r=!0)})),r&&n.push(t);else{if("MultiPoint"!==t.geometry.type)throw new Error("Input geometry must be a Point or MultiPoint");var i=[];q(e,(function(e){R(t,(function(t){ye(t,e)&&(r=!0,i.push(t))}))})),r&&n.push(d(i))}})),f(n)}function me(t,e,n){void 0===n&&(n={});var r=K(t),i=K(e),o=I(i[1]-r[1]),s=I(i[0]-r[0]),a=I(r[1]),u=I(i[1]),l=Math.pow(Math.sin(o/2),2)+Math.pow(Math.sin(s/2),2)*Math.cos(a)*Math.cos(u);return m(2*Math.atan2(Math.sqrt(l),Math.sqrt(1-l)),n.units)}function xe(t,e){var n=!1;return f(function(t){if(t.length<3)return[];t.sort(be);var e,n,r,i,o,s,a=t.length-1,u=t[a].x,l=t[0].x,c=t[a].y,h=c,p=1e-12;for(;a--;)t[a].yh&&(h=t[a].y);var f,g=l-u,d=h-c,y=g>d?g:d,v=.5*(l+u),_=.5*(h+c),m=[new Ee({__sentinel:!0,x:v-20*y,y:_-y},{__sentinel:!0,x:v,y:_+20*y},{__sentinel:!0,x:v+20*y,y:_-y})],x=[],E=[];a=t.length;for(;a--;){for(E.length=0,f=m.length;f--;)(g=t[a].x-m[f].x)>0&&g*g>m[f].r?(x.push(m[f]),m.splice(f,1)):g*g+(d=t[a].y-m[f].y)*d>m[f].r||(E.push(m[f].a,m[f].b,m[f].b,m[f].c,m[f].c,m[f].a),m.splice(f,1));for(we(E),f=E.length;f;)n=E[--f],e=E[--f],r=t[a],i=n.x-e.x,o=n.y-e.y,s=2*(i*(r.y-n.y)-o*(r.x-n.x)),Math.abs(s)>p&&m.push(new Ee(e,n,r))}Array.prototype.push.apply(x,m),a=x.length;for(;a--;)(x[a].a.__sentinel||x[a].b.__sentinel||x[a].c.__sentinel)&&x.splice(a,1);return x}(t.features.map((function(t){var r={x:t.geometry.coordinates[0],y:t.geometry.coordinates[1]};return e?r.z=t.properties[e]:3===t.geometry.coordinates.length&&(n=!0,r.z=t.geometry.coordinates[2]),r}))).map((function(t){var e=[t.a.x,t.a.y],r=[t.b.x,t.b.y],i=[t.c.x,t.c.y],o={};return n?(e.push(t.a.z),r.push(t.b.z),i.push(t.c.z)):o={a:t.a.z,b:t.b.z,c:t.c.z},l([[e,r,i,e]],o)})))}ne.default=re;var Ee=function(t,e,n){this.a=t,this.b=e,this.c=n;var r,i,o=e.x-t.x,s=e.y-t.y,a=n.x-t.x,u=n.y-t.y,l=o*(t.x+e.x)+s*(t.y+e.y),c=a*(t.x+n.x)+u*(t.y+n.y),h=2*(o*(n.y-e.y)-s*(n.x-e.x));this.x=(u*l-s*c)/h,this.y=(o*c-a*l)/h,r=this.x-t.x,i=this.y-t.y,this.r=r*r+i*i};function be(t,e){return e.x-t.x}function we(t){var e,n,r,i,o,s=t.length;t:for(;s;)for(n=t[--s],e=t[--s],r=s;r;)if(o=t[--r],e===(i=t[--r])&&n===o||e===o&&n===i){t.splice(s,2),t.splice(r,2),s-=2;continue t}}function Ie(t){if(!t)throw new Error("geojson is required");switch(t.type){case"Feature":return Ne(t);case"FeatureCollection":return function(t){var e={type:"FeatureCollection"};return Object.keys(t).forEach((function(n){switch(n){case"type":case"features":return;default:e[n]=t[n]}})),e.features=t.features.map((function(t){return Ne(t)})),e}(t);case"Point":case"LineString":case"Polygon":case"MultiPoint":case"MultiLineString":case"MultiPolygon":case"GeometryCollection":return Ce(t);default:throw new Error("unknown GeoJSON type")}}function Ne(t){var e={type:"Feature"};return Object.keys(t).forEach((function(n){switch(n){case"type":case"properties":case"geometry":return;default:e[n]=t[n]}})),e.properties=Se(t.properties),e.geometry=Ce(t.geometry),e}function Se(t){var e={};return t?(Object.keys(t).forEach((function(n){var r=t[n];"object"==typeof r?null===r?e[n]=null:Array.isArray(r)?e[n]=r.map((function(t){return t})):e[n]=Se(r):e[n]=r})),e):e}function Ce(t){var e={type:t.type};return t.bbox&&(e.bbox=t.bbox),"GeometryCollection"===t.type?(e.geometries=t.geometries.map((function(t){return Ce(t)})),e):(e.coordinates=Pe(t.coordinates),e)}function Pe(t){var e=t;return"object"!=typeof e[0]?e.slice():e.map((function(t){return Pe(t)}))}function Me(t,e){if(void 0===e&&(e={}),!P(e=e||{}))throw new Error("options is invalid");var n=e.mutate;if("FeatureCollection"!==it(t))throw new Error("geojson must be a FeatureCollection");if(!t.features.length)throw new Error("geojson is empty");!1!==n&&void 0!==n||(t=Ie(t));var r=[],i=Y(t,(function(t,e){var n=function(t,e){var n,r=t.geometry.coordinates,i=e.geometry.coordinates,o=Le(r[0]),s=Le(r[r.length-1]),a=Le(i[0]),u=Le(i[i.length-1]);if(o===u)n=i.concat(r.slice(1));else if(a===s)n=r.concat(i.slice(1));else if(o===a)n=r.slice(1).reverse().concat(i);else{if(s!==u)return null;n=r.concat(i.reverse().slice(1))}return h(n)}(t,e);return n||(r.push(t),e)}));return i&&r.push(i),r.length?1===r.length?r[0]:g(r.map((function(t){return t.coordinates}))):null}function Le(t){return t[0].toString()+","+t[1].toString()}function Oe(t){return t}function Re(t,e){var n=function(t){if(null==t)return Oe;var e,n,r=t.scale[0],i=t.scale[1],o=t.translate[0],s=t.translate[1];return function(t,a){a||(e=n=0);var u=2,l=t.length,c=new Array(l);for(c[0]=(e+=t[0])*r+o,c[1]=(n+=t[1])*i+s;u1)for(var o,a,u=1,l=s(i[0]);ul&&(a=i[0],i[0]=i[u],i[u]=a,l=o);return i})).filter((function(t){return t.length>0}))}}var De=Object.prototype.hasOwnProperty;function Fe(t,e,n,r,i,o){3===arguments.length&&(r=o=Array,i=null);for(var s=new r(t=1<=t)throw new Error("full hashmap");c=s[l=l+1&u]}return s[l]=r,a[l]=o,o}function h(r,o){for(var l=e(r)&u,c=s[l],h=0;c!=i;){if(n(c,r))return a[l];if(++h>=t)throw new Error("full hashmap");c=s[l=l+1&u]}return s[l]=r,a[l]=o,o}function p(r,o){for(var l=e(r)&u,c=s[l],h=0;c!=i;){if(n(c,r))return a[l];if(++h>=t)break;c=s[l=l+1&u]}return o}function f(){for(var t=[],e=0,n=s.length;e>7^Be[2]^Be[3])}function je(t){var e,n,r,i,o=t.coordinates,s=t.lines,a=t.rings,u=function(){for(var t=Fe(1.4*o.length,E,b,Int32Array,-1,Int32Array),e=new Int32Array(o.length),n=0,r=o.length;n=0){var o=h[n];i===e&&o===r||i===r&&o===e||(++f,p[n]=1)}else c[n]=e,h[n]=r}}function E(t){return ze(o[t])}function b(t,e){return ke(o[t],o[e])}l=c=h=null;var w,I=function(t,e,n,r,i){3===arguments.length&&(r=Array,i=null);for(var o=new r(t=1<=t)throw new Error("full hashset");u=o[a=a+1&s]}return o[a]=r,!0}function l(r){for(var a=e(r)&s,u=o[a],l=0;u!=i;){if(n(u,r))return!0;if(++l>=t)break;u=o[a=a+1&s]}return!1}function c(){for(var t=[],e=0,n=o.length;e>1);er&&(r=o),si&&(i=s)}function u(t){t.forEach(a)}function l(t){t.forEach(u)}for(var c in t)o(t[c]);return r>=e&&i>=n?[e,n,r,i]:void 0}(t=Xe(t)),r=e>0&&n&&function(t,e,n){var r=e[0],i=e[1],o=e[2],s=e[3],a=o-r?(n-1)/(o-r):1,u=s-i?(n-1)/(s-i):1;function l(t){return[Math.round((t[0]-r)*a),Math.round((t[1]-i)*u)]}function c(t,e){for(var n,o,s,l,c,h=-1,p=0,f=t.length,g=new Array(f);++h2&&rn(n[i-3],n[i-1],n[i-2])&&n.splice(n.length-2,1))}if(n.push(e[e.length-1]),i=n.length,nn(e[0],e[e.length-1])&&i<4)throw new Error("invalid polygon");return rn(n[i-3],n[i-1],n[i-2])&&n.splice(n.length-2,1),n}function nn(t,e){return t[0]===e[0]&&t[1]===e[1]}function rn(t,e,n){var r=n[0],i=n[1],o=t[0],s=t[1],a=e[0],u=e[1],l=a-o,c=u-s;return 0===(r-o)*c-(i-s)*l&&(Math.abs(l)>=Math.abs(c)?l>0?o<=r&&r<=a:a<=r&&r<=o:c>0?s<=i&&i<=u:u<=i&&i<=s)}function on(t,e,n){var r=e.x,i=e.y,o=n.x-r,s=n.y-i;if(0!==o||0!==s){var a=((t.x-r)*o+(t.y-i)*s)/(o*o+s*s);a>1?(r=n.x,i=n.y):a>0&&(r+=o*a,i+=s*a)}return(o=t.x-r)*o+(s=t.y-i)*s}function sn(t,e,n,r,i){for(var o,s=r,a=e+1;as&&(o=a,s=u)}s>r&&(o-e>1&&sn(t,e,o,r,i),i.push(t[o]),n-o>1&&sn(t,o,n,r,i))}function an(t,e){var n=t.length-1,r=[t[0]];return sn(t,0,n,e,r),r.push(t[n]),r}function un(t,e,n){if(t.length<=2)return t;var r=void 0!==e?e*e:1;return t=an(t=n?t:function(t,e){for(var n,r,i,o,s,a=t[0],u=[a],l=1,c=t.length;le&&(u.push(n),a=n);return a!==n&&u.push(n),u}(t,r),r)}function ln(t,e,n){return un(t.map((function(t){return{x:t[0],y:t[1],z:t[2]}})),e,n).map((function(t){return t.z?[t.x,t.y,t.z]:[t.x,t.y]}))}function cn(t,e,n){return t.map((function(t){var r=t.map((function(t){return{x:t[0],y:t[1]}}));if(r.length<4)throw new Error("invalid polygon");for(var i=un(r,e,n).map((function(t){return[t.x,t.y]}));!hn(i);)i=un(r,e-=.01*e,n).map((function(t){return[t.x,t.y]}));return i[i.length-1][0]===i[0][0]&&i[i.length-1][1]===i[0][1]||i.push(i[0]),i}))}function hn(t){return!(t.length<3)&&!(3===t.length&&t[2][0]===t[0][0]&&t[2][1]===t[0][1])}var pn=function(){function t(t){this.points=t.points||[],this.duration=t.duration||1e4,this.sharpness=t.sharpness||.85,this.centers=[],this.controls=[],this.stepLength=t.stepLength||60,this.length=this.points.length,this.delay=0;for(var e=0;et&&(e.push(r),n=i)}return e},t.prototype.vector=function(t){var e=this.pos(t+10),n=this.pos(t-10);return{angle:180*Math.atan2(e.y-n.y,e.x-n.x)/3.14,speed:Math.sqrt((n.x-e.x)*(n.x-e.x)+(n.y-e.y)*(n.y-e.y)+(n.z-e.z)*(n.z-e.z))}},t.prototype.pos=function(t){var e=t-this.delay;e<0&&(e=0),e>this.duration&&(e=this.duration-1);var n=e/this.duration;if(n>=1)return this.points[this.length-1];var r=Math.floor((this.points.length-1)*n);return function(t,e,n,r,i){var o=function(t){var e=t*t;return[e*t,3*e*(1-t),3*t*(1-t)*(1-t),(1-t)*(1-t)*(1-t)]}(t);return{x:i.x*o[0]+r.x*o[1]+n.x*o[2]+e.x*o[3],y:i.y*o[0]+r.y*o[1]+n.y*o[2]+e.y*o[3],z:i.z*o[0]+r.z*o[1]+n.z*o[2]+e.z*o[3]}}((this.length-1)*n-r,this.points[r],this.controls[r][1],this.controls[r+1][0],this.points[r+1])},t}();function fn(t,e){void 0===e&&(e={});for(var n=e.resolution||1e4,r=e.sharpness||.85,i=[],o=rt(t).coordinates.map((function(t){return{x:t[0],y:t[1]}})),s=new pn({duration:n,points:o,sharpness:r}),a=function(t){var e=s.pos(t);Math.floor(t/100)%2==0&&i.push([e.x,e.y])},u=0;u=me(t.slice(0,2),[e,i])){var o=(n+i)/2;return[e,o-(r-e)/2,r,o+(r-e)/2]}var s=(e+r)/2;return[s-(i-n)/2,n,s+(i-n)/2,i]}function vn(t,e,n,r){void 0===r&&(r={});var i=K(t),o=I(i[0]),s=I(i[1]),u=I(n),l=x(e,r.units),c=Math.asin(Math.sin(s)*Math.cos(l)+Math.cos(s)*Math.sin(l)*Math.cos(u));return a([w(o+Math.atan2(Math.sin(u)*Math.sin(l)*Math.cos(s),Math.cos(l)-Math.sin(s)*Math.sin(c))),w(c)],r.properties)}function _n(t,e,n){void 0===n&&(n={});for(var r=n.steps||64,i=n.properties?n.properties:!Array.isArray(t)&&"Feature"===t.type&&t.properties?t.properties:{},o=[],s=0;s80*n){r=o=t[0],i=s=t[1];for(var g=n;go&&(o=a),u>s&&(s=u);l=0!==(l=Math.max(o-r,s-i))?1/l:0}return Pn(p,f,n,r,i,l),f}function Sn(t,e,n,r,i){var o,s;if(i===Hn(t,e,n,r)>0)for(o=e;o=e;o-=r)s=Vn(o,t[o],t[o+1],s);return s&&Bn(s,s.next)&&(Xn(s),s=s.next),s}function Cn(t,e){if(!t)return t;e||(e=t);var n,r=t;do{if(n=!1,r.steiner||!Bn(r,r.next)&&0!==qn(r.prev,r,r.next))r=r.next;else{if(Xn(r),(r=e=r.prev)===r.next)break;n=!0}}while(n||r!==e);return e}function Pn(t,e,n,r,i,o,s){if(t){!s&&o&&function(t,e,n,r){var i=t;do{null===i.z&&(i.z=Dn(i.x,i.y,e,n,r)),i.prevZ=i.prev,i.nextZ=i.next,i=i.next}while(i!==t);i.prevZ.nextZ=null,i.prevZ=null,function(t){var e,n,r,i,o,s,a,u,l=1;do{for(n=t,t=null,o=null,s=0;n;){for(s++,r=n,a=0,e=0;e0||u>0&&r;)0!==a&&(0===u||!r||n.z<=r.z)?(i=n,n=n.nextZ,a--):(i=r,r=r.nextZ,u--),o?o.nextZ=i:t=i,i.prevZ=o,o=i;n=r}o.nextZ=null,l*=2}while(s>1)}(i)}(t,r,i,o);for(var a,u,l=t;t.prev!==t.next;)if(a=t.prev,u=t.next,o?Ln(t,r,i,o):Mn(t))e.push(a.i/n),e.push(t.i/n),e.push(u.i/n),Xn(t),t=u.next,l=u.next;else if((t=u)===l){s?1===s?Pn(t=On(t,e,n),e,n,r,i,o,2):2===s&&Rn(t,e,n,r,i,o):Pn(Cn(t),e,n,r,i,o,1);break}}}function Mn(t){var e=t.prev,n=t,r=t.next;if(qn(e,n,r)>=0)return!1;for(var i=t.next.next;i!==t.prev;){if(kn(e.x,e.y,n.x,n.y,r.x,r.y,i.x,i.y)&&qn(i.prev,i,i.next)>=0)return!1;i=i.next}return!0}function Ln(t,e,n,r){var i=t.prev,o=t,s=t.next;if(qn(i,o,s)>=0)return!1;for(var a=i.xo.x?i.x>s.x?i.x:s.x:o.x>s.x?o.x:s.x,c=i.y>o.y?i.y>s.y?i.y:s.y:o.y>s.y?o.y:s.y,h=Dn(a,u,e,n,r),p=Dn(l,c,e,n,r),f=t.prevZ,g=t.nextZ;f&&f.z>=h&&g&&g.z<=p;){if(f!==t.prev&&f!==t.next&&kn(i.x,i.y,o.x,o.y,s.x,s.y,f.x,f.y)&&qn(f.prev,f,f.next)>=0)return!1;if(f=f.prevZ,g!==t.prev&&g!==t.next&&kn(i.x,i.y,o.x,o.y,s.x,s.y,g.x,g.y)&&qn(g.prev,g,g.next)>=0)return!1;g=g.nextZ}for(;f&&f.z>=h;){if(f!==t.prev&&f!==t.next&&kn(i.x,i.y,o.x,o.y,s.x,s.y,f.x,f.y)&&qn(f.prev,f,f.next)>=0)return!1;f=f.prevZ}for(;g&&g.z<=p;){if(g!==t.prev&&g!==t.next&&kn(i.x,i.y,o.x,o.y,s.x,s.y,g.x,g.y)&&qn(g.prev,g,g.next)>=0)return!1;g=g.nextZ}return!0}function On(t,e,n){var r=t;do{var i=r.prev,o=r.next.next;!Bn(i,o)&&zn(i,r,r.next,o)&&jn(i,o)&&jn(o,i)&&(e.push(i.i/n),e.push(r.i/n),e.push(o.i/n),Xn(r),Xn(r.next),r=t=o),r=r.next}while(r!==t);return r}function Rn(t,e,n,r,i,o){var s=t;do{for(var a=s.next.next;a!==s.prev;){if(s.i!==a.i&&Gn(s,a)){var u=Un(s,a);return s=Cn(s,s.next),u=Cn(u,u.next),Pn(s,e,n,r,i,o),void Pn(u,e,n,r,i,o)}a=a.next}s=s.next}while(s!==t)}function Tn(t,e){return t.x-e.x}function An(t,e){if(e=function(t,e){var n,r=e,i=t.x,o=t.y,s=-1/0;do{if(o<=r.y&&o>=r.next.y&&r.next.y!==r.y){var a=r.x+(o-r.y)*(r.next.x-r.x)/(r.next.y-r.y);if(a<=i&&a>s){if(s=a,a===i){if(o===r.y)return r;if(o===r.next.y)return r.next}n=r.x=r.x&&r.x>=c&&i!==r.x&&kn(on.x)&&jn(r,t)&&(n=r,p=u),r=r.next;return n}(t,e)){var n=Un(e,t);Cn(n,n.next)}}function Dn(t,e,n,r,i){return(t=1431655765&((t=858993459&((t=252645135&((t=16711935&((t=32767*(t-n)*i)|t<<8))|t<<4))|t<<2))|t<<1))|(e=1431655765&((e=858993459&((e=252645135&((e=16711935&((e=32767*(e-r)*i)|e<<8))|e<<4))|e<<2))|e<<1))<<1}function Fn(t){var e=t,n=t;do{e.x=0&&(t-s)*(r-a)-(n-s)*(e-a)>=0&&(n-s)*(o-a)-(i-s)*(r-a)>=0}function Gn(t,e){return t.next.i!==e.i&&t.prev.i!==e.i&&!function(t,e){var n=t;do{if(n.i!==t.i&&n.next.i!==t.i&&n.i!==e.i&&n.next.i!==e.i&&zn(n,n.next,t,e))return!0;n=n.next}while(n!==t);return!1}(t,e)&&jn(t,e)&&jn(e,t)&&function(t,e){var n=t,r=!1,i=(t.x+e.x)/2,o=(t.y+e.y)/2;do{n.y>o!=n.next.y>o&&n.next.y!==n.y&&i<(n.next.x-n.x)*(o-n.y)/(n.next.y-n.y)+n.x&&(r=!r),n=n.next}while(n!==t);return r}(t,e)}function qn(t,e,n){return(e.y-t.y)*(n.x-e.x)-(e.x-t.x)*(n.y-e.y)}function Bn(t,e){return t.x===e.x&&t.y===e.y}function zn(t,e,n,r){return!!(Bn(t,e)&&Bn(n,r)||Bn(t,r)&&Bn(n,e))||qn(t,e,n)>0!=qn(t,e,r)>0&&qn(n,r,t)>0!=qn(n,r,e)>0}function jn(t,e){return qn(t.prev,t,t.next)<0?qn(t,e,t.next)>=0&&qn(t,t.prev,e)>=0:qn(t,e,t.prev)<0||qn(t,t.next,e)<0}function Un(t,e){var n=new Yn(t.i,t.x,t.y),r=new Yn(e.i,e.x,e.y),i=t.next,o=e.prev;return t.next=e,e.prev=t,n.next=i,i.prev=n,r.next=n,n.prev=r,o.next=r,r.prev=o,r}function Vn(t,e,n,r){var i=new Yn(t,e,n);return r?(i.next=r.next,i.prev=r,r.next.prev=i,r.next=i):(i.prev=i,i.next=i),i}function Xn(t){t.next.prev=t.prev,t.prev.next=t.next,t.prevZ&&(t.prevZ.nextZ=t.nextZ),t.nextZ&&(t.nextZ.prevZ=t.prevZ)}function Yn(t,e,n){this.i=t,this.x=e,this.y=n,this.prev=null,this.next=null,this.z=null,this.prevZ=null,this.nextZ=null,this.steiner=!1}function Hn(t,e,n,r){for(var i=0,o=e,s=n-r;o0&&(r+=t[i-1].length,n.holes.push(r))}return n}(t),n=wn(e.vertices,e.holes,2),r=[],i=[];n.forEach((function(t,r){var o=n[r];i.push([e.vertices[2*o],e.vertices[2*o+1]])}));for(var o=0;oi?n:i,r>o?r:o]}(t,r),n.push(i),r})),n})(n,t.properties).forEach((function(t){t.id=e.length,e.push(t)}))}))}}(t,e)})),f(e)}Nn.deviation=function(t,e,n,r){var i=e&&e.length,o=i?e[0]*n:t.length,s=Math.abs(Hn(t,0,o,n));if(i)for(var a=0,u=e.length;a0&&(r+=t[i-1].length,n.holes.push(r))}return n},wn.default=In;var Kn=Bt((function(t,e){function n(t,e,n){void 0===n&&(n={});var r={type:"Feature"};return(0===n.id||n.id)&&(r.id=n.id),n.bbox&&(r.bbox=n.bbox),r.properties=e||{},r.geometry=t,r}function r(t,e,r){if(void 0===r&&(r={}),!t)throw new Error("coordinates is required");if(!Array.isArray(t))throw new Error("coordinates must be an Array");if(t.length<2)throw new Error("coordinates must be at least 2 numbers long");if(!f(t[0])||!f(t[1]))throw new Error("coordinates must contain numbers");return n({type:"Point",coordinates:t},e,r)}function i(t,e,r){void 0===r&&(r={});for(var i=0,o=t;i=0))throw new Error("precision must be a positive number");var n=Math.pow(10,e||0);return Math.round(t*n)/n},e.radiansToLength=c,e.lengthToRadians=h,e.lengthToDegrees=function(t,e){return p(h(t,e))},e.bearingToAzimuth=function(t){var e=t%360;return e<0&&(e+=360),e},e.radiansToDegrees=p,e.degreesToRadians=function(t){return t%360*Math.PI/180},e.convertLength=function(t,e,n){if(void 0===e&&(e="kilometers"),void 0===n&&(n="kilometers"),!(t>=0))throw new Error("length must be a positive number");return c(h(t,e),n)},e.convertArea=function(t,n,r){if(void 0===n&&(n="meters"),void 0===r&&(r="kilometers"),!(t>=0))throw new Error("area must be a positive number");var i=e.areaFactors[n];if(!i)throw new Error("invalid original units");var o=e.areaFactors[r];if(!o)throw new Error("invalid final units");return t/i*o},e.isNumber=f,e.isObject=function(t){return!!t&&t.constructor===Object},e.validateBBox=function(t){if(!t)throw new Error("bbox is required");if(!Array.isArray(t))throw new Error("bbox must be an Array");if(4!==t.length&&6!==t.length)throw new Error("bbox must be an Array of 4 or 6 numbers");t.forEach((function(t){if(!f(t))throw new Error("bbox must only contain numbers")}))},e.validateId=function(t){if(!t)throw new Error("id is required");if(-1===["string","number"].indexOf(typeof t))throw new Error("id must be a number or a string")}}));function Qn(t,e,n){if(null!==t)for(var r,i,o,s,a,u,l,c,h=0,p=0,f=t.type,g="FeatureCollection"===f,d="Feature"===f,y=g?t.features.length:1,v=0;va||p>u||f>l)return s=o,a=n,u=p,l=f,void(i=0);var g=Kn.lineString([s,o],t.properties);if(!1===e(g,n,r,f,i))return!1;i++,s=o}))&&void 0}}}))}function ir(t,e){if(!t)throw new Error("geojson is required");nr(t,(function(t,n,r){if(null!==t.geometry){var i=t.geometry.type,o=t.geometry.coordinates;switch(i){case"LineString":if(!1===e(t,n,r,0,0))return!1;break;case"Polygon":for(var s=0;st[0]&&(e[0]=t[0]),e[1]>t[1]&&(e[1]=t[1]),e[2] line1 must only contain 2 coordinates");if(2!==r.length)throw new Error(" line2 must only contain 2 coordinates");var i=n[0][0],o=n[0][1],s=n[1][0],u=n[1][1],l=r[0][0],c=r[0][1],h=r[1][0],p=r[1][1],f=(p-c)*(s-i)-(h-l)*(u-o),g=(h-l)*(o-c)-(p-c)*(i-l),d=(s-i)*(o-c)-(u-o)*(i-l);if(0===f)return null;var y=g/f,v=d/f;return y>=0&&y<=1&&v>=0&&v<=1?a([i+y*(s-i),o+y*(u-o)]):null}function Tr(t,e,n){void 0===n&&(n={});var r=a([1/0,1/0],{dist:1/0}),i=0;return z(t,(function(t){for(var o=Q(t),s=0;s0&&((v=y.features[0]).properties.dist=me(e,v,n),v.properties.location=i+me(u,v,n)),u.properties.dist180?-360:i[0]-o[0]>180?360:0,N(function(t,n,r){var i=r=void 0===r?e:Number(r),o=t[1]*Math.PI/180,s=n[1]*Math.PI/180,a=s-o,u=Math.abs(n[0]-t[0])*Math.PI/180;u>Math.PI&&(u-=2*Math.PI);var l=Math.log(Math.tan(s/2+Math.PI/4)/Math.tan(o/2+Math.PI/4)),c=Math.abs(l)>1e-11?a/l:Math.cos(o);return Math.sqrt(a*a+c*c*u*u)*i}(i,o),"meters",r.units)}function Dr(t,e,n){if(void 0===n&&(n={}),n.method||(n.method="geodesic"),n.units||(n.units="kilometers"),!t)throw new Error("pt is required");if(Array.isArray(t)?t=a(t):"Point"===t.type?t=o(t):et(t,"Point","point"),!e)throw new Error("line is required");Array.isArray(e)?e=h(e):"LineString"===e.type?e=o(e):et(e,"LineString","line");var r=1/0,i=t.geometry.coordinates;return U(e,(function(t){var e=t.geometry.coordinates[0],o=t.geometry.coordinates[1],s=function(t,e,n,r){var i=[n[0]-e[0],n[1]-e[1]],o=Fr([t[0]-e[0],t[1]-e[1]],i);if(o<=0)return kr(t,e,{method:r.method,units:"degrees"});var s=Fr(i,i);if(s<=o)return kr(t,n,{method:r.method,units:"degrees"});var a=o/s,u=[e[0]+a*i[0],e[1]+a*i[1]];return kr(t,u,{method:r.method,units:"degrees"})}(i,e,o,n);s=0&&l<=1&&(p.onLine1=!0),c>=0&&c<=1&&(p.onLine2=!0),!(!p.onLine1||!p.onLine2)&&[p.x,p.y])}function qr(t){for(var e=function(t){if("FeatureCollection"!==t.type)return"Feature"!==t.type?f([o(t)]):f([t]);return t}(t),n=xn(e),r=!1,i=0;!r&&i0){e+=Math.abs(Vr(t[0]));for(var n=1;n2){for(s=0;s=c&&p===i.length-1);p++){if(c>e&&0===o.length){if(!(s=e-c))return o.push(i[p]),h(o);a=mn(i[p],i[p-1])-180,u=vn(i[p],s,a,r),o.push(u.geometry.coordinates)}if(c>=n)return(s=n-c)?(a=mn(i[p],i[p-1])-180,u=vn(i[p],s,a,r),o.push(u.geometry.coordinates),h(o)):(o.push(i[p]),h(o));if(c>=e&&o.push(i[p]),p===i.length-1)return h(o);c+=me(i[p],i[p+1],r)}if(ci)return!1}else if(0!==f)return!1;return r?"start"===r?Math.abs(h)>=Math.abs(p)?h>0?a0?u=Math.abs(p)?h>0?a<=o&&o0?u<=s&&s=Math.abs(p)?h>0?a0?u=Math.abs(p)?h>0?a<=o&&o<=l:l<=o&&o<=a:p>0?u<=s&&s<=c:c<=s&&s<=u}function Zr(t,e){var n=rt(t),r=rt(e),i=n.type,o=r.type;switch(i){case"Point":switch(o){case"MultiPoint":return function(t,e){var n,r=!1;for(n=0;ne[0])&&(!(t[2]e[1])&&!(t[3] is required");if("number"!=typeof n)throw new Error(" must be a number");if("number"!=typeof r)throw new Error(" must be a number");!1!==i&&void 0!==i||(t=JSON.parse(JSON.stringify(t)));var o=Math.pow(10,n);return R(t,(function(t){!function(t,e,n){t.length>n&&t.splice(n,t.length);for(var r=0;r=1||u<=0||l>=1||l<=0))){var d=g,y=!o[d];y&&(o[d]=!0),e?i.push(e(g,t,n,c,h,u,s,a,p,f,l,y)):i.push(g)}}function d(t,e){var n,i,o,s,a=r[t][e],u=r[t][e+1];return a[0]p[e.isect].coord?-1:1}));for(u=[];b.length>0;){var C=b.pop(),P=C.isect,M=C.parent,L=C.winding,O=u.length,R=[p[P].coord],T=P;if(p[P].ringAndEdge1Walkable)var A=p[P].ringAndEdge1,D=p[P].nxtIsectAlongRingAndEdge1;else A=p[P].ringAndEdge2,D=p[P].nxtIsectAlongRingAndEdge2;for(;!ci(p[P].coord,p[D].coord);){R.push(p[D].coord);var F=void 0;for(r=0;r1)for(e=0;e=0==e}function li(t){for(var e=0,n=0;n1&&n.push(h(l)),f(n)}function xi(t,e){if(!e.features.length)throw new Error("lines must contain features");if(1===e.features.length)return e.features[0];var n,r=1/0;return F(e,(function(e){var i=Tr(e,t).properties.dist;ic&&f.push(vn(t,e,c,i).geometry.coordinates),h(f,u)}function wi(t){var e=t%360;return e<0&&(e+=360),e}function Ii(t,e){void 0===e&&(e={});var n=rt(t);switch(e.properties||"Feature"!==t.type||(e.properties=t.properties),n.type){case"Polygon":return Ni(n,e);case"MultiPolygon":return function(t,e){void 0===e&&(e={});var n=rt(t).coordinates,r=e.properties?e.properties:"Feature"===t.type?t.properties:{},i=[];return n.forEach((function(t){i.push(Si(t,r))})),f(i)}(n,e);default:throw new Error("invalid poly")}}function Ni(t,e){return void 0===e&&(e={}),Si(rt(t).coordinates,e.properties?e.properties:"Feature"===t.type?t.properties:{})}function Si(t,e){return t.length>1?g(t,e):h(t[0],e)}function Ci(t,e){var n,r,i;void 0===e&&(e={});var o=e.properties,s=null===(n=e.autoComplete)||void 0===n||n,a=null===(r=e.orderCoords)||void 0===r||r;switch(null!==(i=e.mutate)&&void 0!==i&&i||(t=Ie(t)),t.type){case"FeatureCollection":var u=[];return t.features.forEach((function(t){u.push(Q(Pi(t,{},s,a)))})),y(u,o);default:return Pi(t,o,s,a)}}function Pi(t,e,n,r){e=e||("Feature"===t.type?t.properties:{});var i=rt(t),o=i.coordinates,s=i.type;if(!o.length)throw new Error("line must contain coordinates");switch(s){case"LineString":return n&&(o=Mi(o)),l([o],e);case"MultiLineString":var a=[],u=0;return o.forEach((function(t){if(n&&(t=Mi(t)),r){var e=function(t){var e=t[0],n=t[1],r=t[2],i=t[3];return Math.abs(e-r)*Math.abs(n-i)}(Z(h(t)));e>u?(a.unshift(t),u=e):a.push(t)}else a.push(t)})),l(a,e);default:throw new Error("geometry type "+s+" is not supported")}}function Mi(t){var e=t[0],n=e[0],r=e[1],i=t[t.length-1],o=i[0],s=i[1];return n===o&&r===s||t.push(e),t}function Li(t,e){var n,r,i,o,s,a,u;for(r=1;r<=8;r*=2){for(n=[],o=!(Ri(i=t[t.length-1],e)&r),s=0;se[2]&&(n|=2),t[1]e[3]&&(n|=8),n}function Ti(t,e){for(var n=[],r=0,i=t;r0&&(o[0][0]===o[o.length-1][0]&&o[0][1]===o[o.length-1][1]||o.push(o[0]),o.length>=4&&n.push(o))}return n}vi.prototype.interpolate=function(t){var e=Math.sin((1-t)*this.g)/Math.sin(this.g),n=Math.sin(t*this.g)/Math.sin(this.g),r=e*Math.cos(this.start.y)*Math.cos(this.start.x)+n*Math.cos(this.end.y)*Math.cos(this.end.x),i=e*Math.cos(this.start.y)*Math.sin(this.start.x)+n*Math.cos(this.end.y)*Math.sin(this.end.x),o=e*Math.sin(this.start.y)+n*Math.sin(this.end.y),s=fi*Math.atan2(o,Math.sqrt(Math.pow(r,2)+Math.pow(i,2)));return[fi*Math.atan2(i,r),s]},vi.prototype.Arc=function(t,e){var n=[];if(!t||t<=2)n.push([this.start.lon,this.start.lat]),n.push([this.end.lon,this.end.lat]);else for(var r=1/(t-1),i=0;ip&&(d>c&&gc&&du&&(u=y)}var v=[];if(a&&u0&&Math.abs(x-n[m-1][0])>p){var E=parseFloat(n[m-1][0]),b=parseFloat(n[m-1][1]),w=parseFloat(n[m][0]),I=parseFloat(n[m][1]);if(E>-180&&E-180&&n[m-1][0]c&&E<180&&-180===w&&m+1c&&n[m-1][0]<180){_.push([180,n[m][1]]),m++,_.push([n[m][0],n[m][1]]);continue}if(Ec){var N=E;E=w,w=N;var S=b;b=I,I=S}if(E>c&&w=180&&Ec?180:-180,P]),(_=[]).push([n[m-1][0]>c?-180:180,P]),v.push(_)}else _=[],v.push(_);_.push([x,n[m][1]])}else _.push([n[m][0],n[m][1]])}}else{var M=[];v.push(M);for(var L=0;L=0;a--)if(l[a]!=c[a])return!1;for(a=l.length-1;a>=0;a--)if(u=l[a],!n(t[u],o[u],s))return!1;return typeof t==typeof o}(t,o,s))};function r(t){return null==t}function i(t){return!(!t||"object"!=typeof t||"number"!=typeof t.length)&&("function"==typeof t.copy&&"function"==typeof t.slice&&!(t.length>0&&"number"!=typeof t[0]))}}));function ki(t,e,n){if(void 0===n&&(n={}),!P(n=n||{}))throw new Error("options is invalid");var r,i=n.tolerance||0,o=[],s=Mr(),a=Zn(t);return s.load(a),U(e,(function(t){var e=!1;t&&(F(s.search(t),(function(n){if(!1===e){var o=Q(t).sort(),s=Q(n).sort();Fi(o,s)||(0===i?Wr(o[0],n)&&Wr(o[1],n):Tr(n,o[0]).properties.dist<=i&&Tr(n,o[1]).properties.dist<=i)?(e=!0,r=r?Gi(r,t):t):(0===i?Wr(s[0],t)&&Wr(s[1],t):Tr(t,s[0]).properties.dist<=i&&Tr(t,s[1]).properties.dist<=i)&&(r=r?Gi(r,n):n)}})),!1===e&&r&&(o.push(r),r=void 0))})),r&&o.push(r),f(o)}function Gi(t,e){var n=Q(e),r=Q(t),i=r[0],o=r[r.length-1],s=t.geometry.coordinates;return Fi(n[0],i)?s.unshift(n[1]):Fi(n[0],o)?s.push(n[1]):Fi(n[1],i)?s.unshift(n[0]):Fi(n[1],o)&&s.push(n[0]),t}function qi(t){var e=t%360;return e<0&&(e+=360),e}function Bi(t,e,n){var r;return void 0===n&&(n={}),(r=n.final?zi(K(e),K(t)):zi(K(t),K(e)))>180?-(360-r):r}function zi(t,e){var n=I(t[1]),r=I(e[1]),i=I(e[0]-t[0]);i>Math.PI&&(i-=2*Math.PI),i<-Math.PI&&(i+=2*Math.PI);var o=Math.log(Math.tan(r/2+Math.PI/4)/Math.tan(n/2+Math.PI/4));return(w(Math.atan2(i,o))+360)%360}function ji(t,n,r,i){void 0===i&&(i={});var o=n<0,s=N(Math.abs(n),i.units,"meters");o&&(s=-Math.abs(s));var u=K(t),l=function(t,n,r,i){i=void 0===i?e:Number(i);var o=n/i,s=t[0]*Math.PI/180,a=I(t[1]),u=I(r),l=o*Math.cos(u),c=a+l;Math.abs(c)>Math.PI/2&&(c=c>0?Math.PI-c:-Math.PI-c);var h=Math.log(Math.tan(c/2+Math.PI/4)/Math.tan(a/2+Math.PI/4)),p=Math.abs(h)>1e-11?l/h:Math.cos(a),f=o*Math.sin(u)/p;return[(180*(s+f)/Math.PI+540)%360-180,180*c/Math.PI]}(u,s,r);return l[0]+=l[0]-u[0]>180?-360:u[0]-l[0]>180?360:0,a(l,i.properties)}function Ui(t,e,n,r,i,o){for(var s=0;s0?Xi(e,a,i)<0||(i=a):n>0&&r<=0&&(Vi(e,a,o)||(o=a)),n=r}return[i,o]}function Vi(t,e,n){return Xi(t,e,n)>0}function Xi(t,e,n){return(e[0]-t[0])*(n[1]-t[1])-(n[0]-t[0])*(e[1]-t[1])}function Yi(t){for(var e,n,r=Q(t),i=0,o=1;o0}function Hi(t,e){switch("Feature"===t.type?t.geometry.type:t.type){case"GeometryCollection":return q(t,(function(t){Hi(t,e)})),t;case"LineString":return Wi(Q(t),e),t;case"Polygon":return Ji(Q(t),e),t;case"MultiLineString":return Q(t).forEach((function(t){Wi(t,e)})),t;case"MultiPolygon":return Q(t).forEach((function(t){Ji(t,e)})),t;case"Point":case"MultiPoint":return t}}function Wi(t,e){Yi(t)===e&&t.reverse()}function Ji(t,e){Yi(t[0])!==e&&t[0].reverse();for(var n=1;n + * v. 1.2.0 + * https://github.com/RaumZeit/MarchingSquares.js + * + * MarchingSquaresJS is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MarchingSquaresJS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * As additional permission under GNU Affero General Public License version 3 + * section 7, third-party projects (personal or commercial) may distribute, + * include, or link against UNMODIFIED VERSIONS of MarchingSquaresJS without the + * requirement that said third-party project for that reason alone becomes + * subject to any requirement of the GNU Affero General Public License version 3. + * Any modifications to MarchingSquaresJS, however, must be shared with the public + * and made available. + * + * In summary this: + * - allows you to use MarchingSquaresJS at no cost + * - allows you to use MarchingSquaresJS for both personal and commercial purposes + * - allows you to distribute UNMODIFIED VERSIONS of MarchingSquaresJS under any + * license as long as this license notice is included + * - enables you to keep the source code of your program that uses MarchingSquaresJS + * undisclosed + * - forces you to share any modifications you have made to MarchingSquaresJS, + * e.g. bug-fixes + * + * You should have received a copy of the GNU Affero General Public License + * along with MarchingSquaresJS. If not, see . + */(t,r),s=[],a=0;as?128:64,l|=hs?32:16,l|=ps?8:4;var g=+(l|=fs?2:1),d=0;if(17===l||18===l||33===l||34===l||38===l||68===l||72===l||98===l||102===l||132===l||136===l||137===l||152===l||153===l){var y=(c+h+p+f)/4;d=y>s?2:y0?(l=156,d=4):l=152:33===l?d>0?(l=139,d=4):l=137:72===l?d>0?(l=99,d=4):l=98:132===l&&(d>0?(l=39,d=4):l=38)}if(0!=l&&170!=l){var v,_,m,x,E,b,w,I;v=_=m=x=E=b=w=I=.5;var N=[];1===l?(m=1-Vo(e,p,f),I=1-Vo(e,c,f),N.push(Go[l])):169===l?(m=Vo(s,f,p),I=Vo(s,f,c),N.push(Go[l])):4===l?(b=1-Vo(e,h,p),x=Vo(e,f,p),N.push(Fo[l])):166===l?(b=Vo(s,p,h),x=1-Vo(s,p,f),N.push(Fo[l])):16===l?(E=Vo(e,p,h),_=Vo(e,c,h),N.push(Do[l])):154===l?(E=1-Vo(s,h,p),_=1-Vo(s,h,c),N.push(Do[l])):64===l?(w=Vo(e,f,c),v=1-Vo(e,h,c),N.push(Bo[l])):106===l?(w=1-Vo(s,c,f),v=Vo(s,c,h),N.push(Bo[l])):168===l?(x=Vo(s,f,p),m=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),N.push(ko[l]),N.push(Go[l])):2===l?(x=1-Vo(e,p,f),m=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),N.push(ko[l]),N.push(Go[l])):162===l?(E=Vo(s,p,h),b=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),N.push(ko[l]),N.push(Go[l])):8===l?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),N.push(Do[l]),N.push(Fo[l])):138===l?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),v=1-Vo(s,h,c),_=1-Vo(e,h,c),N.push(Do[l]),N.push(Fo[l])):32===l?(E=Vo(s,p,h),b=Vo(e,p,h),v=Vo(e,c,h),_=Vo(s,c,h),N.push(Do[l]),N.push(Fo[l])):42===l?(I=1-Vo(s,c,f),w=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h),N.push(qo[l]),N.push(Bo[l])):128===l&&(I=Vo(e,f,c),w=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c),N.push(qo[l]),N.push(Bo[l])),5===l?(b=1-Vo(e,h,p),I=1-Vo(e,c,f),N.push(Fo[l])):165===l?(b=Vo(s,p,h),I=Vo(s,f,c),N.push(Fo[l])):20===l?(x=Vo(e,f,p),_=Vo(e,c,h),N.push(ko[l])):150===l?(x=1-Vo(s,p,f),_=1-Vo(s,h,c),N.push(ko[l])):80===l?(E=Vo(e,p,h),w=Vo(e,f,c),N.push(Do[l])):90===l?(E=1-Vo(s,h,p),w=1-Vo(s,c,f),N.push(Do[l])):65===l?(m=1-Vo(e,p,f),v=1-Vo(e,h,c),N.push(Go[l])):105===l?(m=Vo(s,f,p),v=Vo(s,c,h),N.push(Go[l])):160===l?(E=Vo(s,p,h),b=Vo(e,p,h),I=Vo(e,f,c),w=Vo(s,f,c),N.push(Do[l]),N.push(Fo[l])):10===l?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),I=1-Vo(s,c,f),w=1-Vo(e,c,f),N.push(Do[l]),N.push(Fo[l])):130===l?(x=1-Vo(e,p,f),m=1-Vo(s,p,f),v=1-Vo(s,h,c),_=1-Vo(e,h,c),N.push(ko[l]),N.push(Go[l])):40===l?(x=Vo(s,f,p),m=Vo(e,f,p),v=Vo(e,c,h),_=Vo(s,c,h),N.push(ko[l]),N.push(Go[l])):101===l?(b=Vo(s,p,h),v=Vo(s,c,h),N.push(Fo[l])):69===l?(b=1-Vo(e,h,p),v=1-Vo(e,h,c),N.push(Fo[l])):149===l?(I=Vo(s,f,c),_=1-Vo(s,h,c),N.push(qo[l])):21===l?(I=1-Vo(e,c,f),_=Vo(e,c,h),N.push(qo[l])):86===l?(x=1-Vo(s,p,f),w=1-Vo(s,c,f),N.push(ko[l])):84===l?(x=Vo(e,f,p),w=Vo(e,f,c),N.push(ko[l])):89===l?(E=1-Vo(s,h,p),m=Vo(s,f,p),N.push(Go[l])):81===l?(E=Vo(e,p,h),m=1-Vo(e,p,f),N.push(Go[l])):96===l?(E=Vo(s,p,h),b=Vo(e,p,h),w=Vo(e,f,c),v=Vo(s,c,h),N.push(Do[l]),N.push(Fo[l])):74===l?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),w=1-Vo(s,c,f),v=1-Vo(e,h,c),N.push(Do[l]),N.push(Fo[l])):24===l?(E=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),_=Vo(e,c,h),N.push(Do[l]),N.push(Go[l])):146===l?(E=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),_=1-Vo(s,h,c),N.push(Do[l]),N.push(Go[l])):6===l?(b=1-Vo(e,h,p),x=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),N.push(Fo[l]),N.push(ko[l])):164===l?(b=Vo(s,p,h),x=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),N.push(Fo[l]),N.push(ko[l])):129===l?(m=1-Vo(e,p,f),I=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c),N.push(Go[l]),N.push(qo[l])):41===l?(m=Vo(s,f,p),I=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h),N.push(Go[l]),N.push(qo[l])):66===l?(x=1-Vo(e,p,f),m=1-Vo(s,p,f),w=1-Vo(s,c,f),v=1-Vo(e,h,c),N.push(ko[l]),N.push(Go[l])):104===l?(x=Vo(s,f,p),m=Vo(e,f,p),w=Vo(e,f,c),v=Vo(s,c,h),N.push(Go[l]),N.push(zo[l])):144===l?(E=Vo(e,p,h),I=Vo(e,f,c),w=Vo(s,f,c),_=1-Vo(s,h,c),N.push(Do[l]),N.push(Bo[l])):26===l?(E=1-Vo(s,h,p),I=1-Vo(s,c,f),w=1-Vo(e,c,f),_=Vo(e,c,h),N.push(Do[l]),N.push(Bo[l])):36===l?(b=Vo(s,p,h),x=Vo(e,f,p),v=Vo(e,c,h),_=Vo(s,c,h),N.push(Fo[l]),N.push(ko[l])):134===l?(b=1-Vo(e,h,p),x=1-Vo(s,p,f),v=1-Vo(s,h,c),_=1-Vo(e,h,c),N.push(Fo[l]),N.push(ko[l])):9===l?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),m=Vo(s,f,p),I=1-Vo(e,c,f),N.push(Do[l]),N.push(Fo[l])):161===l?(E=Vo(s,p,h),b=Vo(e,p,h),m=1-Vo(e,p,f),I=Vo(s,f,c),N.push(Do[l]),N.push(Fo[l])):37===l?(b=Vo(s,p,h),I=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h),N.push(Fo[l]),N.push(qo[l])):133===l?(b=1-Vo(e,h,p),I=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c),N.push(Fo[l]),N.push(qo[l])):148===l?(x=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),_=1-Vo(s,h,c),N.push(ko[l]),N.push(Bo[l])):22===l?(x=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),_=Vo(e,c,h),N.push(ko[l]),N.push(Bo[l])):82===l?(E=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),w=1-Vo(s,c,f),N.push(Do[l]),N.push(Go[l])):88===l?(E=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),w=Vo(e,f,c),N.push(Do[l]),N.push(Go[l])):73===l?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),m=Vo(s,f,p),v=1-Vo(e,h,c),N.push(Do[l]),N.push(Fo[l])):97===l?(E=Vo(s,p,h),b=Vo(e,p,h),m=1-Vo(e,p,f),v=Vo(s,c,h),N.push(Do[l]),N.push(Fo[l])):145===l?(E=Vo(e,p,h),m=1-Vo(e,p,f),I=Vo(s,f,c),_=1-Vo(s,h,c),N.push(Do[l]),N.push(qo[l])):25===l?(E=1-Vo(s,h,p),m=Vo(s,f,p),I=1-Vo(e,c,f),_=Vo(e,c,h),N.push(Do[l]),N.push(qo[l])):70===l?(b=1-Vo(e,h,p),x=1-Vo(s,p,f),w=1-Vo(s,c,f),v=1-Vo(e,h,c),N.push(Fo[l]),N.push(ko[l])):100===l?(b=Vo(s,p,h),x=Vo(e,f,p),w=Vo(e,f,c),v=Vo(s,c,h),N.push(Fo[l]),N.push(ko[l])):34===l?(0===d?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)):(E=Vo(s,p,h),b=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)),N.push(Do[l]),N.push(Fo[l]),N.push(qo[l]),N.push(Bo[l])):35===l?(4===d?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)):(E=Vo(s,p,h),b=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)),N.push(Do[l]),N.push(Fo[l]),N.push(Go[l]),N.push(Bo[l])):136===l?(0===d?(E=Vo(s,p,h),b=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)):(E=1-Vo(e,h,p),b=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)),N.push(Do[l]),N.push(Fo[l]),N.push(qo[l]),N.push(Bo[l])):153===l?(0===d?(E=Vo(e,p,h),m=1-Vo(e,p,f),I=1-Vo(e,c,f),_=Vo(e,c,h)):(E=1-Vo(s,h,p),m=Vo(s,f,p),I=Vo(s,f,c),_=1-Vo(s,h,c)),N.push(Do[l]),N.push(Go[l])):102===l?(0===d?(b=1-Vo(e,h,p),x=Vo(e,f,p),w=Vo(e,f,c),v=1-Vo(e,h,c)):(b=Vo(s,p,h),x=1-Vo(s,p,f),w=1-Vo(s,c,f),v=Vo(s,c,h)),N.push(Fo[l]),N.push(Bo[l])):155===l?(4===d?(E=Vo(e,p,h),m=1-Vo(e,p,f),I=1-Vo(e,c,f),_=Vo(e,c,h)):(E=1-Vo(s,h,p),m=Vo(s,f,p),I=Vo(s,f,c),_=1-Vo(s,h,c)),N.push(Do[l]),N.push(qo[l])):103===l?(4===d?(b=1-Vo(e,h,p),x=Vo(e,f,p),w=Vo(e,f,c),v=1-Vo(e,h,c)):(b=Vo(s,p,h),x=1-Vo(s,p,f),w=1-Vo(s,c,f),v=Vo(s,c,h)),N.push(Fo[l]),N.push(ko[l])):152===l?(0===d?(E=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),_=Vo(e,c,h)):(E=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),_=1-Vo(s,h,c)),N.push(Do[l]),N.push(ko[l]),N.push(Go[l])):156===l?(4===d?(E=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),_=Vo(e,c,h)):(E=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),_=1-Vo(s,h,c)),N.push(Do[l]),N.push(Go[l]),N.push(Bo[l])):137===l?(0===d?(E=Vo(s,p,h),b=Vo(e,p,h),m=1-Vo(e,p,f),I=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)):(E=1-Vo(e,h,p),b=1-Vo(s,h,p),m=Vo(s,f,p),I=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)),N.push(Do[l]),N.push(Fo[l]),N.push(Go[l])):139===l?(4===d?(E=Vo(s,p,h),b=Vo(e,p,h),m=1-Vo(e,p,f),I=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)):(E=1-Vo(e,h,p),b=1-Vo(s,h,p),m=Vo(s,f,p),I=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)),N.push(Do[l]),N.push(Fo[l]),N.push(qo[l])):98===l?(0===d?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),w=Vo(e,f,c),v=1-Vo(e,h,c)):(E=Vo(s,p,h),b=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),w=1-Vo(s,c,f),v=Vo(s,c,h)),N.push(Do[l]),N.push(Fo[l]),N.push(Bo[l])):99===l?(4===d?(E=1-Vo(e,h,p),b=1-Vo(s,h,p),x=Vo(s,f,p),m=Vo(e,f,p),w=Vo(e,f,c),v=1-Vo(e,h,c)):(E=Vo(s,p,h),b=Vo(e,p,h),x=1-Vo(e,p,f),m=1-Vo(s,p,f),w=1-Vo(s,c,f),v=Vo(s,c,h)),N.push(Do[l]),N.push(Fo[l]),N.push(Go[l])):38===l?(0===d?(b=1-Vo(e,h,p),x=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)):(b=Vo(s,p,h),x=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)),N.push(Fo[l]),N.push(qo[l]),N.push(Bo[l])):39===l?(4===d?(b=1-Vo(e,h,p),x=Vo(e,f,p),I=Vo(e,f,c),w=Vo(s,f,c),v=1-Vo(s,h,c),_=1-Vo(e,h,c)):(b=Vo(s,p,h),x=1-Vo(s,p,f),I=1-Vo(s,c,f),w=1-Vo(e,c,f),v=Vo(e,c,h),_=Vo(s,c,h)),N.push(Fo[l]),N.push(ko[l]),N.push(Bo[l])):85===l&&(E=1,b=0,x=1,m=0,I=0,w=1,v=0,_=1),(v<0||v>1||_<0||_>1||E<0||E>1||x<0||x>1||I<0||I>1||w<0||w>1)&&console.log("MarchingSquaresJS-isoBands: "+l+" "+g+" "+c+","+h+","+p+","+f+" "+d+" "+v+" "+_+" "+E+" "+b+" "+x+" "+m+" "+I+" "+w),o.cells[a][u]={cval:l,cval_real:g,flipped:d,topleft:v,topright:_,righttop:E,rightbottom:b,bottomright:x,bottomleft:m,leftbottom:I,lefttop:w,edges:N}}}}}return o}(t,e,n);return Qi.polygons?(Qi.verbose&&console.log("MarchingSquaresJS-isoBands: returning single polygons for each grid cell"),u=function(t){var e=[],n=0;return t.cells.forEach((function(t,r){t.forEach((function(t,i){if(void 0!==t){var o=Uo[t.cval](t);"object"==typeof o&&Xo(o)?"object"==typeof o[0]&&Xo(o[0])?"object"==typeof o[0][0]&&Xo(o[0][0])?o.forEach((function(t){t.forEach((function(t){t[0]+=i,t[1]+=r})),e[n++]=t})):(o.forEach((function(t){t[0]+=i,t[1]+=r})),e[n++]=o):console.log("MarchingSquaresJS-isoBands: bandcell polygon with malformed coordinates"):console.log("MarchingSquaresJS-isoBands: bandcell polygon with null coordinates")}}))})),e}(l)):(Qi.verbose&&console.log("MarchingSquaresJS-isoBands: returning polygon paths for entire data grid"),u=function(t){for(var e=[],n=t.rows,r=t.cols,i=[],o=0;o0){var a=Ho(t.cells[o][s]),u=null,l=s,c=o;null!==a&&i.push([a.p[0]+l,a.p[1]+c]);do{if(null===(u=Wo(t.cells[c][l],a.x,a.y,a.o)))break;if(i.push([u.p[0]+l,u.p[1]+c]),l+=u.x,a=u,(c+=u.y)<0||c>=n||l<0||l>=r||void 0===t.cells[c][l]){var h=Yo(t,l-=u.x,c-=u.y,u.x,u.y,u.o);if(null===h)break;h.path.forEach((function(t){i.push(t)})),l=h.i,c=h.j,a=h}}while(void 0!==t.cells[c][l]&&t.cells[c][l].edges.length>0);e.push(i),i=[],t.cells[o][s].edges.length>0&&s--}return e}(l)),"function"==typeof Qi.successCallback&&Qi.successCallback(u),u}var to=64,eo=16,no=[],ro=[],io=[],oo=[],so=[],ao=[],uo=[],lo=[],co=[],ho=[],po=[],fo=[],go=[],yo=[],vo=[],_o=[],mo=[],xo=[],Eo=[],bo=[],wo=[],Io=[],No=[],So=[];uo[85]=ho[85]=-1,lo[85]=po[85]=0,co[85]=fo[85]=1,Eo[85]=Io[85]=1,bo[85]=No[85]=0,wo[85]=So[85]=1,no[85]=oo[85]=0,ro[85]=so[85]=-1,io[85]=vo[85]=0,_o[85]=go[85]=0,mo[85]=yo[85]=1,ao[85]=xo[85]=1,Io[1]=Io[169]=0,No[1]=No[169]=-1,So[1]=So[169]=0,go[1]=go[169]=-1,yo[1]=yo[169]=0,vo[1]=vo[169]=0,ho[4]=ho[166]=0,po[4]=po[166]=-1,fo[4]=fo[166]=1,_o[4]=_o[166]=1,mo[4]=mo[166]=0,xo[4]=xo[166]=0,uo[16]=uo[154]=0,lo[16]=lo[154]=1,co[16]=co[154]=1,oo[16]=oo[154]=1,so[16]=so[154]=0,ao[16]=ao[154]=1,Eo[64]=Eo[106]=0,bo[64]=bo[106]=1,wo[64]=wo[106]=0,no[64]=no[106]=-1,ro[64]=ro[106]=0,io[64]=io[106]=1,Eo[2]=Eo[168]=0,bo[2]=bo[168]=-1,wo[2]=wo[168]=1,Io[2]=Io[168]=0,No[2]=No[168]=-1,So[2]=So[168]=0,go[2]=go[168]=-1,yo[2]=yo[168]=0,vo[2]=vo[168]=0,_o[2]=_o[168]=-1,mo[2]=mo[168]=0,xo[2]=xo[168]=1,uo[8]=uo[162]=0,lo[8]=lo[162]=-1,co[8]=co[162]=0,ho[8]=ho[162]=0,po[8]=po[162]=-1,fo[8]=fo[162]=1,go[8]=go[162]=1,yo[8]=yo[162]=0,vo[8]=vo[162]=1,_o[8]=_o[162]=1,mo[8]=mo[162]=0,xo[8]=xo[162]=0,uo[32]=uo[138]=0,lo[32]=lo[138]=1,co[32]=co[138]=1,ho[32]=ho[138]=0,po[32]=po[138]=1,fo[32]=fo[138]=0,no[32]=no[138]=1,ro[32]=ro[138]=0,io[32]=io[138]=0,oo[32]=oo[138]=1,so[32]=so[138]=0,ao[32]=ao[138]=1,Io[128]=Io[42]=0,No[128]=No[42]=1,So[128]=So[42]=1,Eo[128]=Eo[42]=0,bo[128]=bo[42]=1,wo[128]=wo[42]=0,no[128]=no[42]=-1,ro[128]=ro[42]=0,io[128]=io[42]=1,oo[128]=oo[42]=-1,so[128]=so[42]=0,ao[128]=ao[42]=0,ho[5]=ho[165]=-1,po[5]=po[165]=0,fo[5]=fo[165]=0,Io[5]=Io[165]=1,No[5]=No[165]=0,So[5]=So[165]=0,_o[20]=_o[150]=0,mo[20]=mo[150]=1,xo[20]=xo[150]=1,oo[20]=oo[150]=0,so[20]=so[150]=-1,ao[20]=ao[150]=1,uo[80]=uo[90]=-1,lo[80]=lo[90]=0,co[80]=co[90]=1,Eo[80]=Eo[90]=1,bo[80]=bo[90]=0,wo[80]=wo[90]=1,go[65]=go[105]=0,yo[65]=yo[105]=1,vo[65]=vo[105]=0,no[65]=no[105]=0,ro[65]=ro[105]=-1,io[65]=io[105]=0,uo[160]=uo[10]=-1,lo[160]=lo[10]=0,co[160]=co[10]=1,ho[160]=ho[10]=-1,po[160]=po[10]=0,fo[160]=fo[10]=0,Io[160]=Io[10]=1,No[160]=No[10]=0,So[160]=So[10]=0,Eo[160]=Eo[10]=1,bo[160]=bo[10]=0,wo[160]=wo[10]=1,_o[130]=_o[40]=0,mo[130]=mo[40]=1,xo[130]=xo[40]=1,go[130]=go[40]=0,yo[130]=yo[40]=1,vo[130]=vo[40]=0,no[130]=no[40]=0,ro[130]=ro[40]=-1,io[130]=io[40]=0,oo[130]=oo[40]=0,so[130]=so[40]=-1,ao[130]=ao[40]=1,ho[37]=ho[133]=0,po[37]=po[133]=1,fo[37]=fo[133]=1,Io[37]=Io[133]=0,No[37]=No[133]=1,So[37]=So[133]=0,no[37]=no[133]=-1,ro[37]=ro[133]=0,io[37]=io[133]=0,oo[37]=oo[133]=1,so[37]=so[133]=0,ao[37]=ao[133]=0,_o[148]=_o[22]=-1,mo[148]=mo[22]=0,xo[148]=xo[22]=0,Io[148]=Io[22]=0,No[148]=No[22]=-1,So[148]=So[22]=1,Eo[148]=Eo[22]=0,bo[148]=bo[22]=1,wo[148]=wo[22]=1,oo[148]=oo[22]=-1,so[148]=so[22]=0,ao[148]=ao[22]=1,uo[82]=uo[88]=0,lo[82]=lo[88]=-1,co[82]=co[88]=1,_o[82]=_o[88]=1,mo[82]=mo[88]=0,xo[82]=xo[88]=1,go[82]=go[88]=-1,yo[82]=yo[88]=0,vo[82]=vo[88]=1,Eo[82]=Eo[88]=0,bo[82]=bo[88]=-1,wo[82]=wo[88]=0,uo[73]=uo[97]=0,lo[73]=lo[97]=1,co[73]=co[97]=0,ho[73]=ho[97]=0,po[73]=po[97]=-1,fo[73]=fo[97]=0,go[73]=go[97]=1,yo[73]=yo[97]=0,vo[73]=vo[97]=0,no[73]=no[97]=1,ro[73]=ro[97]=0,io[73]=io[97]=1,uo[145]=uo[25]=0,lo[145]=lo[25]=-1,co[145]=co[25]=0,go[145]=go[25]=1,yo[145]=yo[25]=0,vo[145]=vo[25]=1,Io[145]=Io[25]=0,No[145]=No[25]=1,So[145]=So[25]=1,oo[145]=oo[25]=-1,so[145]=so[25]=0,ao[145]=ao[25]=0,ho[70]=ho[100]=0,po[70]=po[100]=1,fo[70]=fo[100]=0,_o[70]=_o[100]=-1,mo[70]=mo[100]=0,xo[70]=xo[100]=1,Eo[70]=Eo[100]=0,bo[70]=bo[100]=-1,wo[70]=wo[100]=1,no[70]=no[100]=1,ro[70]=ro[100]=0,io[70]=io[100]=0,ho[101]=ho[69]=0,po[101]=po[69]=1,fo[101]=fo[69]=0,no[101]=no[69]=1,ro[101]=ro[69]=0,io[101]=io[69]=0,Io[149]=Io[21]=0,No[149]=No[21]=1,So[149]=So[21]=1,oo[149]=oo[21]=-1,so[149]=so[21]=0,ao[149]=ao[21]=0,_o[86]=_o[84]=-1,mo[86]=mo[84]=0,xo[86]=xo[84]=1,Eo[86]=Eo[84]=0,bo[86]=bo[84]=-1,wo[86]=wo[84]=1,uo[89]=uo[81]=0,lo[89]=lo[81]=-1,co[89]=co[81]=0,go[89]=go[81]=1,yo[89]=yo[81]=0,vo[89]=vo[81]=1,uo[96]=uo[74]=0,lo[96]=lo[74]=1,co[96]=co[74]=0,ho[96]=ho[74]=-1,po[96]=po[74]=0,fo[96]=fo[74]=1,Eo[96]=Eo[74]=1,bo[96]=bo[74]=0,wo[96]=wo[74]=0,no[96]=no[74]=1,ro[96]=ro[74]=0,io[96]=io[74]=1,uo[24]=uo[146]=0,lo[24]=lo[146]=-1,co[24]=co[146]=1,_o[24]=_o[146]=1,mo[24]=mo[146]=0,xo[24]=xo[146]=1,go[24]=go[146]=0,yo[24]=yo[146]=1,vo[24]=vo[146]=1,oo[24]=oo[146]=0,so[24]=so[146]=-1,ao[24]=ao[146]=0,ho[6]=ho[164]=-1,po[6]=po[164]=0,fo[6]=fo[164]=1,_o[6]=_o[164]=-1,mo[6]=mo[164]=0,xo[6]=xo[164]=0,Io[6]=Io[164]=0,No[6]=No[164]=-1,So[6]=So[164]=1,Eo[6]=Eo[164]=1,bo[6]=bo[164]=0,wo[6]=wo[164]=0,go[129]=go[41]=0,yo[129]=yo[41]=1,vo[129]=vo[41]=1,Io[129]=Io[41]=0,No[129]=No[41]=1,So[129]=So[41]=0,no[129]=no[41]=-1,ro[129]=ro[41]=0,io[129]=io[41]=0,oo[129]=oo[41]=0,so[129]=so[41]=-1,ao[129]=ao[41]=0,_o[66]=_o[104]=0,mo[66]=mo[104]=1,xo[66]=xo[104]=0,go[66]=go[104]=-1,yo[66]=yo[104]=0,vo[66]=vo[104]=1,Eo[66]=Eo[104]=0,bo[66]=bo[104]=-1,wo[66]=wo[104]=0,no[66]=no[104]=0,ro[66]=ro[104]=-1,io[66]=io[104]=1,uo[144]=uo[26]=-1,lo[144]=lo[26]=0,co[144]=co[26]=0,Io[144]=Io[26]=1,No[144]=No[26]=0,So[144]=So[26]=1,Eo[144]=Eo[26]=0,bo[144]=bo[26]=1,wo[144]=wo[26]=1,oo[144]=oo[26]=-1,so[144]=so[26]=0,ao[144]=ao[26]=1,ho[36]=ho[134]=0,po[36]=po[134]=1,fo[36]=fo[134]=1,_o[36]=_o[134]=0,mo[36]=mo[134]=1,xo[36]=xo[134]=0,no[36]=no[134]=0,ro[36]=ro[134]=-1,io[36]=io[134]=1,oo[36]=oo[134]=1,so[36]=so[134]=0,ao[36]=ao[134]=0,uo[9]=uo[161]=-1,lo[9]=lo[161]=0,co[9]=co[161]=0,ho[9]=ho[161]=0,po[9]=po[161]=-1,fo[9]=fo[161]=0,go[9]=go[161]=1,yo[9]=yo[161]=0,vo[9]=vo[161]=0,Io[9]=Io[161]=1,No[9]=No[161]=0,So[9]=So[161]=1,uo[136]=0,lo[136]=1,co[136]=1,ho[136]=0,po[136]=1,fo[136]=0,_o[136]=-1,mo[136]=0,xo[136]=1,go[136]=-1,yo[136]=0,vo[136]=0,Io[136]=0,No[136]=-1,So[136]=0,Eo[136]=0,bo[136]=-1,wo[136]=1,no[136]=1,ro[136]=0,io[136]=0,oo[136]=1,so[136]=0,ao[136]=1,uo[34]=0,lo[34]=-1,co[34]=0,ho[34]=0,po[34]=-1,fo[34]=1,_o[34]=1,mo[34]=0,xo[34]=0,go[34]=1,yo[34]=0,vo[34]=1,Io[34]=0,No[34]=1,So[34]=1,Eo[34]=0,bo[34]=1,wo[34]=0,no[34]=-1,ro[34]=0,io[34]=1,oo[34]=-1,so[34]=0,ao[34]=0,uo[35]=0,lo[35]=1,co[35]=1,ho[35]=0,po[35]=-1,fo[35]=1,_o[35]=1,mo[35]=0,xo[35]=0,go[35]=-1,yo[35]=0,vo[35]=0,Io[35]=0,No[35]=-1,So[35]=0,Eo[35]=0,bo[35]=1,wo[35]=0,no[35]=-1,ro[35]=0,io[35]=1,oo[35]=1,so[35]=0,ao[35]=1,uo[153]=0,lo[153]=1,co[153]=1,go[153]=-1,yo[153]=0,vo[153]=0,Io[153]=0,No[153]=-1,So[153]=0,oo[153]=1,so[153]=0,ao[153]=1,ho[102]=0,po[102]=-1,fo[102]=1,_o[102]=1,mo[102]=0,xo[102]=0,Eo[102]=0,bo[102]=1,wo[102]=0,no[102]=-1,ro[102]=0,io[102]=1,uo[155]=0,lo[155]=-1,co[155]=0,go[155]=1,yo[155]=0,vo[155]=1,Io[155]=0,No[155]=1,So[155]=1,oo[155]=-1,so[155]=0,ao[155]=0,ho[103]=0,po[103]=1,fo[103]=0,_o[103]=-1,mo[103]=0,xo[103]=1,Eo[103]=0,bo[103]=-1,wo[103]=1,no[103]=1,ro[103]=0,io[103]=0,uo[152]=0,lo[152]=1,co[152]=1,_o[152]=-1,mo[152]=0,xo[152]=1,go[152]=-1,yo[152]=0,vo[152]=0,Io[152]=0,No[152]=-1,So[152]=0,Eo[152]=0,bo[152]=-1,wo[152]=1,oo[152]=1,so[152]=0,ao[152]=1,uo[156]=0,lo[156]=-1,co[156]=1,_o[156]=1,mo[156]=0,xo[156]=1,go[156]=-1,yo[156]=0,vo[156]=0,Io[156]=0,No[156]=-1,So[156]=0,Eo[156]=0,bo[156]=1,wo[156]=1,oo[156]=-1,so[156]=0,ao[156]=1,uo[137]=0,lo[137]=1,co[137]=1,ho[137]=0,po[137]=1,fo[137]=0,go[137]=-1,yo[137]=0,vo[137]=0,Io[137]=0,No[137]=-1,So[137]=0,no[137]=1,ro[137]=0,io[137]=0,oo[137]=1,so[137]=0,ao[137]=1,uo[139]=0,lo[139]=1,co[139]=1,ho[139]=0,po[139]=-1,fo[139]=0,go[139]=1,yo[139]=0,vo[139]=0,Io[139]=0,No[139]=1,So[139]=0,no[139]=-1,ro[139]=0,io[139]=0,oo[139]=1,so[139]=0,ao[139]=1,uo[98]=0,lo[98]=-1,co[98]=0,ho[98]=0,po[98]=-1,fo[98]=1,_o[98]=1,mo[98]=0,xo[98]=0,go[98]=1,yo[98]=0,vo[98]=1,Eo[98]=0,bo[98]=1,wo[98]=0,no[98]=-1,ro[98]=0,io[98]=1,uo[99]=0,lo[99]=1,co[99]=0,ho[99]=0,po[99]=-1,fo[99]=1,_o[99]=1,mo[99]=0,xo[99]=0,go[99]=-1,yo[99]=0,vo[99]=1,Eo[99]=0,bo[99]=-1,wo[99]=0,no[99]=1,ro[99]=0,io[99]=1,ho[38]=0,po[38]=-1,fo[38]=1,_o[38]=1,mo[38]=0,xo[38]=0,Io[38]=0,No[38]=1,So[38]=1,Eo[38]=0,bo[38]=1,wo[38]=0,no[38]=-1,ro[38]=0,io[38]=1,oo[38]=-1,so[38]=0,ao[38]=0,ho[39]=0,po[39]=1,fo[39]=1,_o[39]=-1,mo[39]=0,xo[39]=0,Io[39]=0,No[39]=-1,So[39]=1,Eo[39]=0,bo[39]=1,wo[39]=0,no[39]=-1,ro[39]=0,io[39]=1,oo[39]=1,so[39]=0,ao[39]=0;var Co=function(t){return[[t.bottomleft,0],[0,0],[0,t.leftbottom]]},Po=function(t){return[[1,t.rightbottom],[1,0],[t.bottomright,0]]},Mo=function(t){return[[t.topright,1],[1,1],[1,t.righttop]]},Lo=function(t){return[[0,t.lefttop],[0,1],[t.topleft,1]]},Oo=function(t){return[[t.bottomright,0],[t.bottomleft,0],[0,t.leftbottom],[0,t.lefttop]]},Ro=function(t){return[[t.bottomright,0],[t.bottomleft,0],[1,t.righttop],[1,t.rightbottom]]},To=function(t){return[[1,t.righttop],[1,t.rightbottom],[t.topleft,1],[t.topright,1]]},Ao=function(t){return[[0,t.leftbottom],[0,t.lefttop],[t.topleft,1],[t.topright,1]]},Do=[],Fo=[],ko=[],Go=[],qo=[],Bo=[],zo=[],jo=[];Go[1]=qo[1]=18,Go[169]=qo[169]=18,ko[4]=Fo[4]=12,ko[166]=Fo[166]=12,Do[16]=jo[16]=4,Do[154]=jo[154]=4,Bo[64]=zo[64]=22,Bo[106]=zo[106]=22,ko[2]=Bo[2]=17,Go[2]=qo[2]=18,ko[168]=Bo[168]=17,Go[168]=qo[168]=18,Do[8]=Go[8]=9,Fo[8]=ko[8]=12,Do[162]=Go[162]=9,Fo[162]=ko[162]=12,Do[32]=jo[32]=4,Fo[32]=zo[32]=1,Do[138]=jo[138]=4,Fo[138]=zo[138]=1,qo[128]=jo[128]=21,Bo[128]=zo[128]=22,qo[42]=jo[42]=21,Bo[42]=zo[42]=22,Fo[5]=qo[5]=14,Fo[165]=qo[165]=14,ko[20]=jo[20]=6,ko[150]=jo[150]=6,Do[80]=Bo[80]=11,Do[90]=Bo[90]=11,Go[65]=zo[65]=3,Go[105]=zo[105]=3,Do[160]=Bo[160]=11,Fo[160]=qo[160]=14,Do[10]=Bo[10]=11,Fo[10]=qo[10]=14,ko[130]=jo[130]=6,Go[130]=zo[130]=3,ko[40]=jo[40]=6,Go[40]=zo[40]=3,Fo[101]=zo[101]=1,Fo[69]=zo[69]=1,qo[149]=jo[149]=21,qo[21]=jo[21]=21,ko[86]=Bo[86]=17,ko[84]=Bo[84]=17,Do[89]=Go[89]=9,Do[81]=Go[81]=9,Do[96]=zo[96]=0,Fo[96]=Bo[96]=15,Do[74]=zo[74]=0,Fo[74]=Bo[74]=15,Do[24]=ko[24]=8,Go[24]=jo[24]=7,Do[146]=ko[146]=8,Go[146]=jo[146]=7,Fo[6]=Bo[6]=15,ko[6]=qo[6]=16,Fo[164]=Bo[164]=15,ko[164]=qo[164]=16,Go[129]=jo[129]=7,qo[129]=zo[129]=20,Go[41]=jo[41]=7,qo[41]=zo[41]=20,ko[66]=zo[66]=2,Go[66]=Bo[66]=19,ko[104]=zo[104]=2,Go[104]=Bo[104]=19,Do[144]=qo[144]=10,Bo[144]=jo[144]=23,Do[26]=qo[26]=10,Bo[26]=jo[26]=23,Fo[36]=jo[36]=5,ko[36]=zo[36]=2,Fo[134]=jo[134]=5,ko[134]=zo[134]=2,Do[9]=qo[9]=10,Fo[9]=Go[9]=13,Do[161]=qo[161]=10,Fo[161]=Go[161]=13,Fo[37]=jo[37]=5,qo[37]=zo[37]=20,Fo[133]=jo[133]=5,qo[133]=zo[133]=20,ko[148]=qo[148]=16,Bo[148]=jo[148]=23,ko[22]=qo[22]=16,Bo[22]=jo[22]=23,Do[82]=ko[82]=8,Go[82]=Bo[82]=19,Do[88]=ko[88]=8,Go[88]=Bo[88]=19,Do[73]=zo[73]=0,Fo[73]=Go[73]=13,Do[97]=zo[97]=0,Fo[97]=Go[97]=13,Do[145]=Go[145]=9,qo[145]=jo[145]=21,Do[25]=Go[25]=9,qo[25]=jo[25]=21,Fo[70]=zo[70]=1,ko[70]=Bo[70]=17,Fo[100]=zo[100]=1,ko[100]=Bo[100]=17,Do[34]=Go[34]=9,Fo[34]=ko[34]=12,qo[34]=jo[34]=21,Bo[34]=zo[34]=22,Do[136]=jo[136]=4,Fo[136]=zo[136]=1,ko[136]=Bo[136]=17,Go[136]=qo[136]=18,Do[35]=jo[35]=4,Fo[35]=ko[35]=12,Go[35]=qo[35]=18,Bo[35]=zo[35]=22,Do[153]=jo[153]=4,Go[153]=qo[153]=18,Fo[102]=ko[102]=12,Bo[102]=zo[102]=22,Do[155]=Go[155]=9,qo[155]=jo[155]=23,Fo[103]=zo[103]=1,ko[103]=Bo[103]=17,Do[152]=jo[152]=4,ko[152]=Bo[152]=17,Go[152]=qo[152]=18,Do[156]=ko[156]=8,Go[156]=qo[156]=18,Bo[156]=jo[156]=23,Do[137]=jo[137]=4,Fo[137]=zo[137]=1,Go[137]=qo[137]=18,Do[139]=jo[139]=4,Fo[139]=Go[139]=13,qo[139]=zo[139]=20,Do[98]=Go[98]=9,Fo[98]=ko[98]=12,Bo[98]=zo[98]=22,Do[99]=zo[99]=0,Fo[99]=ko[99]=12,Go[99]=Bo[99]=19,Fo[38]=ko[38]=12,qo[38]=jo[38]=21,Bo[38]=zo[38]=22,Fo[39]=jo[39]=5,ko[39]=qo[39]=16,Bo[39]=zo[39]=22;var Uo=[];function Vo(t,e,n){return(t-e)/(n-e)}function Xo(t){return t.constructor.toString().indexOf("Array")>-1}function Yo(t,e,n,r,i,o){for(var s=t.cells[n][e],a=s.cval_real,u=e+r,l=n+i,c=[],h=!1;!h;){if(void 0===t.cells[l]||void 0===t.cells[l][u])if(l-=i,u-=r,a=(s=t.cells[l][u]).cval_real,-1===i)if(0===o)if(1&a)c.push([u,l]),r=-1,i=0,o=0;else{if(!(4&a)){c.push([u+s.bottomright,l]),r=0,i=1,o=1,h=!0;break}c.push([u+1,l]),r=1,i=0,o=0}else{if(!(1&a)){if(4&a){c.push([u+s.bottomright,l]),r=0,i=1,o=1,h=!0;break}c.push([u+s.bottomleft,l]),r=0,i=1,o=0,h=!0;break}c.push([u,l]),r=-1,i=0,o=0}else if(1===i)if(0===o){if(!(a&eo)){if(a&to){c.push([u+s.topleft,l+1]),r=0,i=-1,o=0,h=!0;break}c.push([u+s.topright,l+1]),r=0,i=-1,o=1,h=!0;break}c.push([u+1,l+1]),r=1,i=0,o=1}else c.push([u+1,l+1]),r=1,i=0,o=1;else if(-1===r)if(0===o){if(!(a&to)){if(1&a){c.push([u,l+s.leftbottom]),r=1,i=0,o=0,h=!0;break}c.push([u,l+s.lefttop]),r=1,i=0,o=1,h=!0;break}c.push([u,l+1]),r=0,i=1,o=0}else{if(!(a&to)){console.log("MarchingSquaresJS-isoBands: wtf");break}c.push([u,l+1]),r=0,i=1,o=0}else{if(1!==r){console.log("MarchingSquaresJS-isoBands: we came from nowhere!");break}if(0===o){if(!(4&a)){c.push([u+1,l+s.rightbottom]),r=-1,i=0,o=0,h=!0;break}c.push([u+1,l]),r=0,i=-1,o=1}else{if(!(4&a)){if(a&eo){c.push([u+1,l+s.righttop]),r=-1,i=0,o=1;break}c.push([u+1,l+s.rightbottom]),r=-1,i=0,o=0,h=!0;break}c.push([u+1,l]),r=0,i=-1,o=1}}else if(a=(s=t.cells[l][u]).cval_real,-1===r)if(0===o)if(void 0!==t.cells[l-1]&&void 0!==t.cells[l-1][u])r=0,i=-1,o=1;else{if(!(1&a)){c.push([u+s.bottomright,l]),r=0,i=1,o=1,h=!0;break}c.push([u,l])}else{if(!(a&to)){console.log("MarchingSquaresJS-isoBands: found entry from top at "+u+","+l);break}console.log("MarchingSquaresJS-isoBands: proceeding in x-direction!")}else if(1===r){if(0===o){console.log("MarchingSquaresJS-isoBands: wtf");break}if(void 0!==t.cells[l+1]&&void 0!==t.cells[l+1][u])r=0,i=1,o=0;else{if(!(a&eo)){c.push([u+s.topleft,l+1]),r=0,i=-1,o=0,h=!0;break}c.push([u+1,l+1]),r=1,i=0,o=1}}else if(-1===i){if(1!==o){console.log("MarchingSquaresJS-isoBands: wtf");break}if(void 0!==t.cells[l][u+1])r=1,i=0,o=1;else{if(!(4&a)){c.push([u+1,l+s.righttop]),r=-1,i=0,o=1,h=!0;break}c.push([u+1,l]),r=0,i=-1,o=1}}else{if(1!==i){console.log("MarchingSquaresJS-isoBands: where did we came from???");break}if(0!==o){console.log("MarchingSquaresJS-isoBands: wtf");break}if(void 0!==t.cells[l][u-1])r=-1,i=0,o=0;else{if(!(a&to)){c.push([u,l+s.leftbottom]),r=1,i=0,o=0,h=!0;break}c.push([u,l+1]),r=0,i=1,o=0}}if(l+=i,(u+=r)===e&&l===n)break}return{path:c,i:u,j:l,x:r,y:i,o:o}}function Ho(t){if(t.edges.length>0){var e=t.edges[t.edges.length-1],n=t.cval_real;switch(e){case 0:return n&eo?{p:[1,t.righttop],x:-1,y:0,o:1}:{p:[t.topleft,1],x:0,y:-1,o:0};case 1:return 4&n?{p:[t.topleft,1],x:0,y:-1,o:0}:{p:[1,t.rightbottom],x:-1,y:0,o:0};case 2:return 4&n?{p:[t.bottomright,0],x:0,y:1,o:1}:{p:[t.topleft,1],x:0,y:-1,o:0};case 3:return 1&n?{p:[t.topleft,1],x:0,y:-1,o:0}:{p:[t.bottomleft,0],x:0,y:1,o:0};case 4:return n&eo?{p:[1,t.righttop],x:-1,y:0,o:1}:{p:[t.topright,1],x:0,y:-1,o:1};case 5:return 4&n?{p:[t.topright,1],x:0,y:-1,o:1}:{p:[1,t.rightbottom],x:-1,y:0,o:0};case 6:return 4&n?{p:[t.bottomright,0],x:0,y:1,o:1}:{p:[t.topright,1],x:0,y:-1,o:1};case 7:return 1&n?{p:[t.topright,1],x:0,y:-1,o:1}:{p:[t.bottomleft,0],x:0,y:1,o:0};case 8:return 4&n?{p:[t.bottomright,0],x:0,y:1,o:1}:{p:[1,t.righttop],x:-1,y:0,o:1};case 9:return 1&n?{p:[1,t.righttop],x:-1,y:0,o:1}:{p:[t.bottomleft,0],x:0,y:1,o:0};case 10:return 1&n?{p:[0,t.leftbottom],x:1,y:0,o:0}:{p:[1,t.righttop],x:-1,y:0,o:1};case 11:return n&to?{p:[1,t.righttop],x:-1,y:0,o:1}:{p:[0,t.lefttop],x:1,y:0,o:1};case 12:return 4&n?{p:[t.bottomright,0],x:0,y:1,o:1}:{p:[1,t.rightbottom],x:-1,y:0,o:0};case 13:return 1&n?{p:[1,t.rightbottom],x:-1,y:0,o:0}:{p:[t.bottomleft,0],x:0,y:1,o:0};case 14:return 1&n?{p:[0,t.leftbottom],x:1,y:0,o:0}:{p:[1,t.rightbottom],x:-1,y:0,o:0};case 15:return n&to?{p:[1,t.rightbottom],x:-1,y:0,o:0}:{p:[0,t.lefttop],x:1,y:0,o:1};case 16:return 4&n?{p:[t.bottomright,0],x:0,y:1,o:1}:{p:[0,t.leftbottom],x:1,y:0,o:0};case 17:return n&to?{p:[t.bottomright,0],x:0,y:1,o:1}:{p:[0,t.lefttop],x:1,y:0,o:1};case 18:return 1&n?{p:[0,t.leftbottom],x:1,y:0,o:0}:{p:[t.bottomleft,0],x:0,y:1,o:0};case 19:return n&to?{p:[t.bottomleft,0],x:0,y:1,o:0}:{p:[0,t.lefttop],x:1,y:0,o:1};case 20:return n&to?{p:[t.topleft,1],x:0,y:-1,o:0}:{p:[0,t.leftbottom],x:1,y:0,o:0};case 21:return n&eo?{p:[0,t.leftbottom],x:1,y:0,o:0}:{p:[t.topright,1],x:0,y:-1,o:1};case 22:return n&to?{p:[t.topleft,1],x:0,y:-1,o:0}:{p:[0,t.lefttop],x:1,y:0,o:1};case 23:return n&eo?{p:[0,t.lefttop],x:1,y:0,o:1}:{p:[t.topright,1],x:0,y:-1,o:1};default:console.log("MarchingSquaresJS-isoBands: edge index out of range!"),console.log(t)}}return null}function Wo(t,e,n,r){var i,o,s,a,u,l=t.cval;switch(e){case-1:switch(r){case 0:i=Fo[l],s=ho[l],a=po[l],u=fo[l];break;default:i=Do[l],s=uo[l],a=lo[l],u=co[l]}break;case 1:switch(r){case 0:i=qo[l],s=Io[l],a=No[l],u=So[l];break;default:i=Bo[l],s=Eo[l],a=bo[l],u=wo[l]}break;default:switch(n){case-1:switch(r){case 0:i=zo[l],s=no[l],a=ro[l],u=io[l];break;default:i=jo[l],s=oo[l],a=so[l],u=ao[l]}break;case 1:switch(r){case 0:i=Go[l],s=go[l],a=yo[l],u=vo[l];break;default:i=ko[l],s=_o[l],a=mo[l],u=xo[l]}}}if(o=t.edges.indexOf(i),void 0===t.edges[o])return null;switch(function(t,e){delete t.edges[e];for(var n=e+1;n0){var a=r[e-1],u=is(n,a);!1!==u&&(a[1]=u,n[0]=u),s.push(a[0]),e===o.length-2&&(s.push(n[0]),s.push(n[1]))}2===o.length&&(s.push(n[0]),s.push(n[1]))}var l,c,h,p,f,g,d,y})),h(s,t.properties)}function ss(t,e,n){var r=e[0]-t[0],i=e[1]-t[1],o=n[0]-e[0];return function(t){return(t>0)-(t<0)||+t}(r*(n[1]-e[1])-o*i)}function as(t,e){return e.geometry.coordinates[0].every((function(e){return ye(a(e),t)}))}Uo[1]=Uo[169]=Co,Uo[4]=Uo[166]=Po,Uo[16]=Uo[154]=Mo,Uo[64]=Uo[106]=Lo,Uo[168]=Uo[2]=Oo,Uo[162]=Uo[8]=Ro,Uo[138]=Uo[32]=To,Uo[42]=Uo[128]=Ao,Uo[5]=Uo[165]=function(t){return[[0,0],[0,t.leftbottom],[1,t.rightbottom],[1,0]]},Uo[20]=Uo[150]=function(t){return[[1,0],[t.bottomright,0],[t.topright,1],[1,1]]},Uo[80]=Uo[90]=function(t){return[[1,1],[1,t.righttop],[0,t.lefttop],[0,1]]},Uo[65]=Uo[105]=function(t){return[[t.bottomleft,0],[0,0],[0,1],[t.topleft,1]]},Uo[160]=Uo[10]=function(t){return[[1,t.righttop],[1,t.rightbottom],[0,t.leftbottom],[0,t.lefttop]]},Uo[130]=Uo[40]=function(t){return[[t.topleft,1],[t.topright,1],[t.bottomright,0],[t.bottomleft,0]]},Uo[85]=function(){return[[0,0],[0,1],[1,1],[1,0]]},Uo[101]=Uo[69]=function(t){return[[1,t.rightbottom],[1,0],[0,0],[0,1],[t.topleft,1]]},Uo[149]=Uo[21]=function(t){return[[t.topright,1],[1,1],[1,0],[0,0],[0,t.leftbottom]]},Uo[86]=Uo[84]=function(t){return[[1,0],[t.bottomright,0],[0,t.lefttop],[0,1],[1,1]]},Uo[89]=Uo[81]=function(t){return[[1,1],[1,t.righttop],[t.bottomleft,0],[0,0],[0,1]]},Uo[96]=Uo[74]=function(t){return[[1,t.righttop],[1,t.rightbottom],[0,t.lefttop],[0,1],[t.topleft,1]]},Uo[24]=Uo[146]=function(t){return[[1,1],[1,t.righttop],[t.bottomright,0],[t.bottomleft,0],[t.topright,1]]},Uo[6]=Uo[164]=function(t){return[[1,t.rightbottom],[1,0],[t.bottomright,0],[0,t.leftbottom],[0,t.lefttop]]},Uo[129]=Uo[41]=function(t){return[[t.topright,1],[t.bottomleft,0],[0,0],[0,t.leftbottom],[t.topleft,1]]},Uo[66]=Uo[104]=function(t){return[[t.bottomright,0],[t.bottomleft,0],[0,t.lefttop],[0,1],[t.topleft,1]]},Uo[144]=Uo[26]=function(t){return[[1,1],[1,t.righttop],[0,t.leftbottom],[0,t.lefttop],[t.topright,1]]},Uo[36]=Uo[134]=function(t){return[[1,t.rightbottom],[1,0],[t.bottomright,0],[t.topleft,1],[t.topright,1]]},Uo[9]=Uo[161]=function(t){return[[1,t.righttop],[1,t.rightbottom],[t.bottomleft,0],[0,0],[0,t.leftbottom]]},Uo[37]=Uo[133]=function(t){return[[1,t.rightbottom],[1,0],[0,0],[0,t.leftbottom],[t.topleft,1],[t.topright,1]]},Uo[148]=Uo[22]=function(t){return[[1,1],[1,0],[t.bottomright,0],[0,t.leftbottom],[0,t.lefttop],[t.topright,1]]},Uo[82]=Uo[88]=function(t){return[[1,1],[1,t.righttop],[t.bottomright,0],[t.bottomleft,0],[0,t.lefttop],[0,1]]},Uo[73]=Uo[97]=function(t){return[[1,t.righttop],[1,t.rightbottom],[t.bottomleft,0],[0,0],[0,1],[t.topleft,1]]},Uo[145]=Uo[25]=function(t){return[[1,1],[1,t.righttop],[t.bottomleft,0],[0,0],[0,t.leftbottom],[t.topright,1]]},Uo[70]=Uo[100]=function(t){return[[1,t.rightbottom],[1,0],[t.bottomright,0],[0,t.lefttop],[0,1],[t.topleft,1]]},Uo[34]=function(t){return[Ao(t),Ro(t)]},Uo[35]=function(t){return[[1,t.righttop],[1,t.rightbottom],[t.bottomright,0],[t.bottomleft,0],[0,t.leftbottom],[0,t.lefttop],[t.topleft,1],[t.topright,1]]},Uo[136]=function(t){return[To(t),Oo(t)]},Uo[153]=function(t){return[Mo(t),Co(t)]},Uo[102]=function(t){return[Po(t),Lo(t)]},Uo[155]=function(t){return[[1,1],[1,t.righttop],[t.bottomleft,0],[0,0],[0,t.leftbottom],[t.topright,1]]},Uo[103]=function(t){return[[1,t.rightbottom],[1,0],[t.bottomright,0],[0,t.lefttop],[0,1],[t.topleft,1]]},Uo[152]=function(t){return[Mo(t),Oo(t)]},Uo[156]=function(t){return[[1,1],[1,t.righttop],[t.bottomright,0],[t.bottomleft,0],[0,t.leftbottom],[0,t.lefttop],[t.topright,1]]},Uo[137]=function(t){return[To(t),Co(t)]},Uo[139]=function(t){return[[1,t.righttop],[1,t.rightbottom],[t.bottomleft,0],[0,0],[0,t.leftbottom],[t.topleft,1],[t.topright,1]]},Uo[98]=function(t){return[Ro(t),Lo(t)]},Uo[99]=function(t){return[[1,t.righttop],[1,t.rightbottom],[t.bottomright,0],[t.bottomleft,0],[0,t.lefttop],[0,1],[t.topleft,1]]},Uo[38]=function(t){return[Po(t),Ao(t)]},Uo[39]=function(t){return[[1,t.rightbottom],[1,0],[t.bottomright,0],[0,t.leftbottom],[0,t.lefttop],[t.topleft,1],[t.topright,1]]};var us=function(){function t(e){this.id=t.buildId(e),this.coordinates=e,this.innerEdges=[],this.outerEdges=[],this.outerEdgesSorted=!1}return t.buildId=function(t){return t.join(",")},t.prototype.removeInnerEdge=function(t){this.innerEdges=this.innerEdges.filter((function(e){return e.from.id!==t.from.id}))},t.prototype.removeOuterEdge=function(t){this.outerEdges=this.outerEdges.filter((function(e){return e.to.id!==t.to.id}))},t.prototype.addOuterEdge=function(t){this.outerEdges.push(t),this.outerEdgesSorted=!1},t.prototype.sortOuterEdges=function(){var t=this;this.outerEdgesSorted||(this.outerEdges.sort((function(e,n){var r=e.to,i=n.to;if(r.coordinates[0]-t.coordinates[0]>=0&&i.coordinates[0]-t.coordinates[0]<0)return 1;if(r.coordinates[0]-t.coordinates[0]<0&&i.coordinates[0]-t.coordinates[0]>=0)return-1;if(r.coordinates[0]-t.coordinates[0]==0&&i.coordinates[0]-t.coordinates[0]==0)return r.coordinates[1]-t.coordinates[1]>=0||i.coordinates[1]-t.coordinates[1]>=0?r.coordinates[1]-i.coordinates[1]:i.coordinates[1]-r.coordinates[1];var o=ss(t.coordinates,r.coordinates,i.coordinates);return o<0?1:o>0?-1:Math.pow(r.coordinates[0]-t.coordinates[0],2)+Math.pow(r.coordinates[1]-t.coordinates[1],2)-(Math.pow(i.coordinates[0]-t.coordinates[0],2)+Math.pow(i.coordinates[1]-t.coordinates[1],2))})),this.outerEdgesSorted=!0)},t.prototype.getOuterEdges=function(){return this.sortOuterEdges(),this.outerEdges},t.prototype.getOuterEdge=function(t){return this.sortOuterEdges(),this.outerEdges[t]},t.prototype.addInnerEdge=function(t){this.innerEdges.push(t)},t}(),ls=function(){function t(t,e){this.from=t,this.to=e,this.next=void 0,this.label=void 0,this.symetric=void 0,this.ring=void 0,this.from.addOuterEdge(this),this.to.addInnerEdge(this)}return t.prototype.getSymetric=function(){return this.symetric||(this.symetric=new t(this.to,this.from),this.symetric.symetric=this),this.symetric},t.prototype.deleteEdge=function(){this.from.removeOuterEdge(this),this.to.removeInnerEdge(this)},t.prototype.isEqual=function(t){return this.from.id===t.from.id&&this.to.id===t.to.id},t.prototype.toString=function(){return"Edge { "+this.from.id+" -> "+this.to.id+" }"},t.prototype.toLineString=function(){return h([this.from.coordinates,this.to.coordinates])},t.prototype.compareTo=function(t){return ss(t.from.coordinates,t.to.coordinates,this.to.coordinates)},t}(),cs=function(){function t(){this.edges=[],this.polygon=void 0,this.envelope=void 0}return t.prototype.push=function(t){this.edges.push(t),this.polygon=this.envelope=void 0},t.prototype.get=function(t){return this.edges[t]},Object.defineProperty(t.prototype,"length",{get:function(){return this.edges.length},enumerable:!0,configurable:!0}),t.prototype.forEach=function(t){this.edges.forEach(t)},t.prototype.map=function(t){return this.edges.map(t)},t.prototype.some=function(t){return this.edges.some(t)},t.prototype.isValid=function(){return!0},t.prototype.isHole=function(){var t=this,e=this.edges.reduce((function(e,n,r){return n.from.coordinates[1]>t.edges[e].from.coordinates[1]&&(e=r),e}),0),n=(0===e?this.length:e)-1,r=(e+1)%this.length,i=ss(this.edges[n].from.coordinates,this.edges[e].from.coordinates,this.edges[r].from.coordinates);return 0===i?this.edges[n].from.coordinates[0]>this.edges[r].from.coordinates[0]:i>0},t.prototype.toMultiPoint=function(){return d(this.edges.map((function(t){return t.from.coordinates})))},t.prototype.toPolygon=function(){if(this.polygon)return this.polygon;var t=this.edges.map((function(t){return t.from.coordinates}));return t.push(this.edges[0].from.coordinates),this.polygon=l([t])},t.prototype.getEnvelope=function(){return this.envelope?this.envelope:this.envelope=dn(this.toPolygon())},t.findEdgeRingContaining=function(t,e){var n,r,i=t.getEnvelope();return e.forEach((function(e){var o,s,u,l,c,h,p=e.getEnvelope();if((r&&(n=r.getEnvelope()),s=i,u=(o=p).geometry.coordinates[0].map((function(t){return t[0]})),l=o.geometry.coordinates[0].map((function(t){return t[1]})),c=s.geometry.coordinates[0].map((function(t){return t[0]})),h=s.geometry.coordinates[0].map((function(t){return t[1]})),Math.max.apply(null,u)!==Math.max.apply(null,c)||Math.max.apply(null,l)!==Math.max.apply(null,h)||Math.min.apply(null,u)!==Math.min.apply(null,c)||Math.min.apply(null,l)!==Math.min.apply(null,h))&&as(p,i)){for(var f=t.map((function(t){return t.from.coordinates})),g=void 0,d=function(t){e.some((function(e){return n=t,r=e.from.coordinates,n[0]===r[0]&&n[1]===r[1];var n,r}))||(g=t)},y=0,v=f;y=0;--o){var s=i[o],a=s.symetric,u=void 0,l=void 0;s.label===e&&(u=s),a.label===e&&(l=a),u&&l&&(l&&(r=l),u&&(r&&(r.next=u,r=void 0),n||(n=u)))}r&&(r.next=n)},t.prototype._findLabeledEdgeRings=function(){var t=[],e=0;return this.edges.forEach((function(n){if(!(n.label>=0)){t.push(n);var r=n;do{r.label=e,r=r.next}while(!n.isEqual(r));e++}})),t},t.prototype.getEdgeRings=function(){var t=this;this._computeNextCWEdges(),this.edges.forEach((function(t){t.label=void 0})),this._findLabeledEdgeRings().forEach((function(e){t._findIntersectionNodes(e).forEach((function(n){t._computeNextCCWEdges(n,e.label)}))}));var e=[];return this.edges.forEach((function(n){n.ring||e.push(t._findEdgeRing(n))})),e},t.prototype._findIntersectionNodes=function(t){var e=[],n=t,r=function(){var r=0;n.from.getOuterEdges().forEach((function(e){e.label===t.label&&++r})),r>1&&e.push(n.from),n=n.next};do{r()}while(!t.isEqual(n));return e},t.prototype._findEdgeRing=function(t){var e=t,n=new cs;do{n.push(e),e.ring=n,e=e.next}while(!t.isEqual(e));return n},t.prototype.removeNode=function(t){var e=this;t.getOuterEdges().forEach((function(t){return e.removeEdge(t)})),t.innerEdges.forEach((function(t){return e.removeEdge(t)})),delete this.nodes[t.id]},t.prototype.removeEdge=function(t){this.edges=this.edges.filter((function(e){return!e.isEqual(t)})),t.deleteEdge()},t}();function ps(t,e){var n=!0;return z(t,(function(t){z(e,(function(e){if(!1===n)return!1;n=function(t,e){switch(t.type){case"Point":switch(e.type){case"Point":return n=t.coordinates,r=e.coordinates,!(n[0]===r[0]&&n[1]===r[1]);case"LineString":return!fs(e,t);case"Polygon":return!ye(t,e)}break;case"LineString":switch(e.type){case"Point":return!fs(t,e);case"LineString":return!function(t,e){if(Or(t,e).features.length>0)return!0;return!1}(t,e);case"Polygon":return!gs(e,t)}break;case"Polygon":switch(e.type){case"Point":return!ye(e,t);case"LineString":return!gs(t,e);case"Polygon":return!function(t,e){for(var n=0,r=t.coordinates[0];n0)return!0;return!1}(e,t)}}var n,r;return!1}(t.geometry,e.geometry)}))})),n}function fs(t,e){for(var n=0;n0}function ds(t,e,n){var r=n[0]-t[0],i=n[1]-t[1],o=e[0]-t[0],s=e[1]-t[1];return 0==r*s-i*o&&(Math.abs(o)>=Math.abs(s)?o>0?t[0]<=n[0]&&n[0]<=e[0]:e[0]<=n[0]&&n[0]<=t[0]:s>0?t[1]<=n[1]&&n[1]<=e[1]:e[1]<=n[1]&&n[1]<=t[1])}function ys(t,e){return!(t[0]>e[0])&&(!(t[2]e[1])&&!(t[3]0}function Es(t,e){for(var n=!1,r=!1,i=t.coordinates.length,o=0;o=Math.abs(a)?s>0?t[0]<=n[0]&&n[0]<=e[0]:e[0]<=n[0]&&n[0]<=t[0]:a>0?t[1]<=n[1]&&n[1]<=e[1]:e[1]<=n[1]&&n[1]<=t[1]:Math.abs(s)>=Math.abs(a)?s>0?t[0]0?t[1]=0&&(n=[].concat(t.slice(r,t.length),t.slice(1,r+1))),n},ws.prototype.comparePath=function(t,e){var n=this;return t.every((function(t,e){return n.compareCoord(t,this[e])}),e)},ws.prototype.comparePolygon=function(t,e){if(this.compareLine(t.coordinates[0],e.coordinates[0],1,!0)){var n=t.coordinates.slice(1,t.coordinates.length),r=e.coordinates.slice(1,e.coordinates.length),i=this;return n.every((function(t){return this.some((function(e){return i.compareLine(t,e,1,!0)}))}),r)}return!1},ws.prototype.compareFeature=function(t,e){return!(t.id!==e.id||!this.objectComparator(t.properties,e.properties)||!this.compareBBox(t,e))&&this.compare(t.geometry,e.geometry)},ws.prototype.compareBBox=function(t,e){return!!(!t.bbox&&!e.bbox||t.bbox&&e.bbox&&this.compareCoord(t.bbox,e.bbox))},ws.prototype.removePseudo=function(t){return t};var Cs=ws;function Ps(t,e){var n=!1;return z(t,(function(t){z(e,(function(e){if(!0===n)return!0;n=!ps(t.geometry,e.geometry)}))})),n}var Ms=Bt((function(t){function e(t,e,n,r){this.dataset=[],this.epsilon=1,this.minPts=2,this.distance=this._euclideanDistance,this.clusters=[],this.noise=[],this._visited=[],this._assigned=[],this._datasetLength=0,this._init(t,e,n,r)}e.prototype.run=function(t,e,n,r){this._init(t,e,n,r);for(var i=0;i=this.minPts&&(e=this._mergeArrays(e,i))}1!==this._assigned[r]&&this._addToCluster(r,t)}},e.prototype._addToCluster=function(t,e){this.clusters[e].push(t),this._assigned[t]=1},e.prototype._regionQuery=function(t){for(var e=[],n=0;n0){for(u=0;u=0);return t},e.prototype.assign=function(){for(var t,e=!1,n=this.dataset.length,r=0;ri&&(n=r):e=this.minPts)return n}},n.prototype._regionQuery=function(t,e){e=e||this.epsilon;for(var n=[],r=0,i=this.dataset.length;r0;r.length0;){var a=t[Math.floor(Math.random()*o)],u=s?a.join("_"):""+a;n[u]||(n[u]=!0,r.push(a))}if(r.length0,s=t[Math.floor(Math.random()*i)];o&&s.join("_");for(r.push(s);r.length0,f=[];if(n)i="kmrand"==n?Gs(t,e):"kmpp"==n?qs(t,e):n;else for(var g={};i.length0;){var u=s.pop();if(u===n)return Vs(u);u.closed=!0;for(var l=t.neighbors(u),c=0,h=l.length;c0)){if(o/=p,p<0){if(o0){if(o>h)return;o>c&&(c=o)}if(o=r-u,p||!(o<0)){if(o/=p,p<0){if(o>h)return;o>c&&(c=o)}else if(p>0){if(o0)){if(o/=f,f<0){if(o0){if(o>h)return;o>c&&(c=o)}if(o=i-l,f||!(o<0)){if(o/=f,f<0){if(o>h)return;o>c&&(c=o)}else if(f>0){if(o0||h<1)||(c>0&&(t[0]=[u+c*p,l+c*f]),h<1&&(t[1]=[u+h*p,l+h*f]),!0)}}}}}function ua(t,e,n,r,i){var o=t[1];if(o)return!0;var s,a,u=t[0],l=t.left,c=t.right,h=l[0],p=l[1],f=c[0],g=c[1],d=(h+f)/2,y=(p+g)/2;if(g===p){if(d=r)return;if(h>f){if(u){if(u[1]>=i)return}else u=[d,n];o=[d,i]}else{if(u){if(u[1]1)if(h>f){if(u){if(u[1]>=i)return}else u=[(n-a)/s,n];o=[(i-a)/s,i]}else{if(u){if(u[1]=r)return}else u=[e,s*e+a];o=[r,s*r+a]}else{if(u){if(u[0]0&&(this.content[0]=e,this.bubbleUp(0)),t},remove:function(t){var e=this.content.indexOf(t),n=this.content.pop();e!==this.content.length-1&&(this.content[e]=n,this.scoreFunction(n)0;){var n=(t+1>>1)-1,r=this.content[n];if(!(this.scoreFunction(e)=-La)){var f=u*u+l*l,g=c*c+h*h,d=(h*f-l*g)/p,y=(u*g-c*f)/p,v=fa.pop()||new ga;v.arc=t,v.site=i,v.x=d+s,v.y=(v.cy=y+a)+Math.sqrt(d*d+y*y),t.circle=v;for(var _=null,m=Ca._;m;)if(v.yMa)a=a.L;else{if(!((i=o-Ia(a,s))>Ma)){r>-Ma?(e=a.P,n=a):i>-Ma?(e=a,n=a.N):e=n=a;break}if(!a.R){e=a;break}a=a.R}!function(t){Sa[t.index]={site:t,halfedges:[]}}(t);var u=ma(t);if(Na.insert(e,u),e||n){if(e===n)return ya(e),n=ma(e.site),Na.insert(u,n),u.edge=n.edge=ia(e.site,u.site),da(e),void da(n);if(n){ya(e),ya(n);var l=e.site,c=l[0],h=l[1],p=t[0]-c,f=t[1]-h,g=n.site,d=g[0]-c,y=g[1]-h,v=2*(p*y-f*d),_=p*p+f*f,m=d*d+y*y,x=[(y*_-f*m)/v+c,(p*m-d*_)/v+h];sa(n.edge,l,g,x),u.edge=ia(l,t,null,x),n.edge=ia(t,g,null,x),da(e),da(n)}else u.edge=ia(e.site,u.site)}}function wa(t,e){var n=t.site,r=n[0],i=n[1],o=i-e;if(!o)return r;var s=t.P;if(!s)return-1/0;var a=(n=s.site)[0],u=n[1],l=u-e;if(!l)return a;var c=a-r,h=1/o-1/l,p=c/l;return h?(-p+Math.sqrt(p*p-2*h*(c*c/(-2*l)-u+l/2+i-o/2)))/h+r:(r+a)/2}function Ia(t,e){var n=t.N;if(n)return wa(n,e);var r=t.site;return r[1]===e?r[0]:1/0}var Na,Sa,Ca,Pa,Ma=1e-6,La=1e-12;function Oa(t,e){return e[1]-t[1]||e[0]-t[0]}function Ra(t,e){var n,r,i,o=t.sort(Oa).pop();for(Pa=[],Sa=new Array(t.length),Na=new $s,Ca=new $s;;)if(i=pa,o&&(!i||o[1]Ma||Math.abs(i[0][1]-i[1][1])>Ma)||delete Pa[o]}(s,a,u,l),function(t,e,n,r){var i,o,s,a,u,l,c,h,p,f,g,d,y=Sa.length,v=!0;for(i=0;iMa||Math.abs(d-p)>Ma)&&(u.splice(a,0,Pa.push(oa(s,f,Math.abs(g-t)Ma?[t,Math.abs(h-t)Ma?[Math.abs(p-r)Ma?[n,Math.abs(h-n)Ma?[Math.abs(p-e)=-270&&(d=-d),g<-180&&g>=-360&&(y=-y),"degrees"===o){var v=d*Math.cos(h)+y*Math.sin(h),_=y*Math.cos(h)-d*Math.sin(h);d=v,y=_}p.push([d+c[0],y+c[1]])}return p.push(p[0]),"degrees"===o?l([p],u):$o(l([p],u),s,{pivot:a})}function Da(t){var e=t*Math.PI/180;return Math.tan(e)}function Fa(t,e){void 0===e&&(e={});var n=0,r=0,i=0;return q(t,(function(t,o,s){var a=e.weight?null==s?void 0:s[e.weight]:void 0;if(!C(a=null==a?1:a))throw new Error("weight value must be a number for feature index "+o);(a=Number(a))>0&&R(t,(function(t){n+=t[0]*a,r+=t[1]*a,i+=a}))})),a([n/i,r/i],e.properties,e)}function ka(t,e,n,r,i){var o=r.tolerance||.001,s=0,u=0,l=0,c=0;if(F(n,(function(e){var n,r=null===(n=e.properties)||void 0===n?void 0:n.weight,i=null==r?1:r;if(!C(i=Number(i)))throw new Error("weight value must be a number");if(i>0){c+=1;var o=i*me(e,t);0===o&&(o=1);var a=i/o;s+=e.geometry.coordinates[0]*a,u+=e.geometry.coordinates[1]*a,l+=a}})),c<1)throw new Error("no features to measure");var h=s/l,p=u/l;return 1===c||0===i||Math.abs(h-e[0])n&&(n=u,r=o,e.push([]));var l=o-r,c=t.coordinates[u][l+1],h=i[0],p=i[1],f=c[0],g=c[1];e[u].push([.75*h+.25*f,.75*p+.25*g]),e[u].push([.25*h+.75*f,.25*p+.75*g])}),!0),e.forEach((function(t){t.push(t[0])}))}function Ba(t,e){var n=0,r=0,i=0;R(t,(function(o,s,a,u,l){u>i&&(i=u,r=s,e.push([[]])),l>n&&(n=l,r=s,e[u].push([]));var c=s-r,h=t.coordinates[u][l][c+1],p=o[0],f=o[1],g=h[0],d=h[1];e[u][l].push([.75*p+.25*g,.75*f+.25*d]),e[u][l].push([.25*p+.75*g,.25*f+.75*d])}),!0),e.forEach((function(t){t.forEach((function(t){t.push(t[0])}))}))}function za(t,e,n){void 0===n&&(n=2);var r=K(t),i=K(e),o=r[0]-i[0],s=r[1]-i[1];return 1===n?Math.abs(o)+Math.abs(s):Math.pow(Math.pow(o,n)+Math.pow(s,n),1/n)}function ja(t,e){var n=(e=e||{}).threshold||1e4,r=e.p||2,i=e.binary||!1,o=e.alpha||-1,s=e.standardization||!1,a=[];F(t,(function(t){a.push(En(t))}));for(var u=[],l=0;l0?1:0}(t[0]))*e,n*Math.log(Math.tan(.25*Math.PI+.5*t[1]*e))];return i[0]>r&&(i[0]=r),i[0]<-r&&(i[0]=-r),i[1]>r&&(i[1]=r),i[1]<-r&&(i[1]=-r),i}function Wa(t){var e=180/Math.PI,n=6378137;return[t[0]*e/n,(.5*Math.PI-2*Math.atan(Math.exp(-t[1]/n)))*e]}Ra.prototype={constructor:Ra,polygons:function(){var t=this.edges;return this.cells.map((function(e){var n=e.halfedges.map((function(n){return ca(e,t[n])}));return n.data=e.site.data,n}))},triangles:function(){var t=[],e=this.edges;return this.cells.forEach((function(n,r){if(o=(i=n.halfedges).length)for(var i,o,s,a,u,l,c=n.site,h=-1,p=e[i[o-1]],f=p.left===c?p.right:p.left;++h=a)return null;var u=t-i.site[0],l=e-i.site[1],c=u*u+l*l;do{i=o.cells[r=s],s=null,i.halfedges.forEach((function(n){var r=o.edges[n],a=r.left;if(a!==i.site&&a||(a=r.right)){var u=t-a[0],l=e-a[1],h=u*u+l*l;h0?t+n[e-1]:t})),o.forEach((function(t){t=2*t*Math.PI/o[o.length-1];var n=Math.random();i.push([n*(e.max_radial_length||10)*Math.sin(t),n*(e.max_radial_length||10)*Math.cos(t)])})),i[i.length-1]=i[0],i=i.map((r=Ka(e.bbox),function(t){return[t[0]+r[0],t[1]+r[1]]})),n.push(l([i]))},i=0;i + * @license MIT + * @preserve + */function fu(t,e){return t>e?1:t0))break;if(null===e.right)break;if(n(t,e.right.key)>0){a=e.right;if(e.right=a.left,a.left=e,null===(e=a).right)break}i.right=e,i=e,e=e.right}}return i.right=e.left,o.left=e.right,e.left=r.right,e.right=r.left,e}function du(t,e,n,r){var i=new pu(t,e);if(null===n)return i.left=i.right=null,i;var o=r(t,(n=gu(t,n,r)).key);return o<0?(i.left=n.left,i.right=n,n.left=null):o>=0&&(i.right=n.right,i.left=n,n.right=null),i}function yu(t,e,n){var r=null,i=null;if(e){var o=n((e=gu(t,e,n)).key,t);0===o?(r=e.left,i=e.right):o<0?(i=e.right,e.right=null,r=e):(r=e.left,e.left=null,i=e)}return{left:r,right:i}}function vu(t,e,n,r,i){if(t){r(e+(n?"└── ":"├── ")+i(t)+"\n");var o=e+(n?" ":"│ ");t.left&&vu(t.left,o,!1,r,i),t.right&&vu(t.right,o,!0,r,i)}}var _u=function(){function t(t){void 0===t&&(t=fu),this._root=null,this._size=0,this._comparator=t}return t.prototype.insert=function(t,e){return this._size++,this._root=du(t,e,this._root,this._comparator)},t.prototype.add=function(t,e){var n=new pu(t,e);null===this._root&&(n.left=n.right=null,this._size++,this._root=n);var r=this._comparator,i=gu(t,this._root,r),o=r(t,i.key);return 0===o?this._root=i:(o<0?(n.left=i.left,n.right=i,i.left=null):o>0&&(n.right=i.right,n.left=i,i.right=null),this._size++,this._root=n),this._root},t.prototype.remove=function(t){this._root=this._remove(t,this._root,this._comparator)},t.prototype._remove=function(t,e,n){var r;return null===e?null:0===n(t,(e=gu(t,e,n)).key)?(null===e.left?r=e.right:(r=gu(t,e.left,n)).right=e.right,this._size--,r):e},t.prototype.pop=function(){var t=this._root;if(t){for(;t.left;)t=t.left;return this._root=gu(t.key,this._root,this._comparator),this._root=this._remove(t.key,this._root,this._comparator),{key:t.key,data:t.data}}return null},t.prototype.findStatic=function(t){for(var e=this._root,n=this._comparator;e;){var r=n(t,e.key);if(0===r)return e;e=r<0?e.left:e.right}return null},t.prototype.find=function(t){return this._root&&(this._root=gu(t,this._root,this._comparator),0!==this._comparator(t,this._root.key))?null:this._root},t.prototype.contains=function(t){for(var e=this._root,n=this._comparator;e;){var r=n(t,e.key);if(0===r)return!0;e=r<0?e.left:e.right}return!1},t.prototype.forEach=function(t,e){for(var n=this._root,r=[],i=!1;!i;)null!==n?(r.push(n),n=n.left):0!==r.length?(n=r.pop(),t.call(e,n),n=n.right):i=!0;return this},t.prototype.range=function(t,e,n,r){for(var i=[],o=this._comparator,s=this._root;0!==i.length||s;)if(s)i.push(s),s=s.left;else{if(o((s=i.pop()).key,e)>0)break;if(o(s.key,t)>=0&&n.call(r,s))return this;s=s.right}return this},t.prototype.keys=function(){var t=[];return this.forEach((function(e){var n=e.key;return t.push(n)})),t},t.prototype.values=function(){var t=[];return this.forEach((function(e){var n=e.data;return t.push(n)})),t},t.prototype.min=function(){return this._root?this.minNode(this._root).key:null},t.prototype.max=function(){return this._root?this.maxNode(this._root).key:null},t.prototype.minNode=function(t){if(void 0===t&&(t=this._root),t)for(;t.left;)t=t.left;return t},t.prototype.maxNode=function(t){if(void 0===t&&(t=this._root),t)for(;t.right;)t=t.right;return t},t.prototype.at=function(t){for(var e=this._root,n=!1,r=0,i=[];!n;)if(e)i.push(e),e=e.left;else if(i.length>0){if(e=i.pop(),r===t)return e;r++,e=e.right}else n=!0;return null},t.prototype.next=function(t){var e=this._root,n=null;if(t.right){for(n=t.right;n.left;)n=n.left;return n}for(var r=this._comparator;e;){var i=r(t.key,e.key);if(0===i)break;i<0?(n=e,e=e.left):e=e.right}return n},t.prototype.prev=function(t){var e=this._root,n=null;if(null!==t.left){for(n=t.left;n.right;)n=n.right;return n}for(var r=this._comparator;e;){var i=r(t.key,e.key);if(0===i)break;i<0?e=e.left:(n=e,e=e.right)}return n},t.prototype.clear=function(){return this._root=null,this._size=0,this},t.prototype.toList=function(){return function(t){var e=t,n=[],r=!1,i=new pu(null,null),o=i;for(;!r;)e?(n.push(e),e=e.left):n.length>0?e=(e=o=o.next=n.pop()).right:r=!0;return o.next=null,i.next}(this._root)},t.prototype.load=function(t,e,n){void 0===e&&(e=[]),void 0===n&&(n=!1);var r=t.length,i=this._comparator;if(n&&Eu(t,e,0,r-1,i),null===this._root)this._root=mu(t,e,0,r),this._size=r;else{var o=function(t,e,n){var r=new pu(null,null),i=r,o=t,s=e;for(;null!==o&&null!==s;)n(o.key,s.key)<0?(i.next=o,o=o.next):(i.next=s,s=s.next),i=i.next;null!==o?i.next=o:null!==s&&(i.next=s);return r.next}(this.toList(),function(t,e){for(var n=new pu(null,null),r=n,i=0;i0){var o=n+Math.floor(i/2),s=t[o],a=e[o],u=new pu(s,a);return u.left=mu(t,e,n,o),u.right=mu(t,e,o+1,r),u}return null}function xu(t,e,n){var r=n-e;if(r>0){var i=e+Math.floor(r/2),o=xu(t,e,i),s=t.head;return s.left=o,t.head=t.head.next,s.right=xu(t,i+1,n),s}return null}function Eu(t,e,n,r,i){if(!(n>=r)){for(var o=t[n+r>>1],s=n-1,a=r+1;;){do{s++}while(i(t[s],o)<0);do{a--}while(i(t[a],o)>0);if(s>=a)break;var u=t[s];t[s]=t[a],t[a]=u,u=e[s],e[s]=e[a],e[a]=u}Eu(t,e,n,a,i),Eu(t,e,a+1,r,i)}}function bu(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function wu(t,e){for(var n=0;ne.x?1:t.ye.y?1:0}}]),Iu(t,[{key:"link",value:function(t){if(t.point===this.point)throw new Error("Tried to link already linked events");for(var e=t.point.events,n=0,r=e.length;n=0&&u>=0?sl?-1:0:o<0&&u<0?sl?1:0:uo?1:0}}}]),t}(),zu=0,ju=function(){function t(e,n,r,i){bu(this,t),this.id=++zu,this.leftSE=e,e.segment=this,e.otherSE=n,this.rightSE=n,n.segment=this,n.otherSE=e,this.rings=r,this.windings=i}return Iu(t,null,[{key:"compare",value:function(t,e){var n=t.leftSE.point.x,r=e.leftSE.point.x,i=t.rightSE.point.x,o=e.rightSE.point.x;if(os&&a>u)return-1;var c=t.comparePoint(e.leftSE.point);if(c<0)return 1;if(c>0)return-1;var h=e.comparePoint(t.rightSE.point);return 0!==h?h:-1}if(n>r){if(sa&&s>l)return 1;var p=e.comparePoint(t.leftSE.point);if(0!==p)return p;var f=t.comparePoint(e.rightSE.point);return f<0?1:f>0?-1:1}if(sa)return 1;if(io){var d=t.comparePoint(e.rightSE.point);if(d<0)return 1;if(d>0)return-1}if(i!==o){var y=u-s,v=i-n,_=l-a,m=o-r;if(y>v&&_m)return-1}return i>o?1:il?1:t.ide.id?1:0}}]),Iu(t,[{key:"replaceRightSE",value:function(t){this.rightSE=t,this.rightSE.segment=this,this.rightSE.otherSE=this.leftSE,this.leftSE.otherSE=this.rightSE}},{key:"bbox",value:function(){var t=this.leftSE.point.y,e=this.rightSE.point.y;return{ll:{x:this.leftSE.point.x,y:te?t:e}}}},{key:"vector",value:function(){return{x:this.rightSE.point.x-this.leftSE.point.x,y:this.rightSE.point.y-this.leftSE.point.y}}},{key:"isAnEndpoint",value:function(t){return t.x===this.leftSE.point.x&&t.y===this.leftSE.point.y||t.x===this.rightSE.point.x&&t.y===this.rightSE.point.y}},{key:"comparePoint",value:function(t){if(this.isAnEndpoint(t))return 0;var e=this.leftSE.point,n=this.rightSE.point,r=this.vector();if(e.x===n.x)return t.x===e.x?0:t.x0&&a.swapEvents(),Bu.comparePoints(this.leftSE.point,this.rightSE.point)>0&&this.swapEvents(),r&&(i.checkForConsuming(),o.checkForConsuming()),n}},{key:"swapEvents",value:function(){var t=this.rightSE;this.rightSE=this.leftSE,this.leftSE=t,this.leftSE.isLeft=!0,this.rightSE.isLeft=!1;for(var e=0,n=this.windings.length;e0){var o=n;n=r,r=o}if(n.prev===r){var s=n;n=r,r=s}for(var a=0,u=r.rings.length;a0))throw new Error("Tried to create degenerate segment at [".concat(e.x,", ").concat(e.y,"]"));i=n,o=e,s=-1}return new t(new Bu(i,!0),new Bu(o,!1),[r],[s])}}]),t}(),Uu=function(){function t(e,n,r){if(bu(this,t),!Array.isArray(e)||0===e.length)throw new Error("Input geometry is not a valid Polygon or MultiPolygon");if(this.poly=n,this.isExterior=r,this.segments=[],"number"!=typeof e[0][0]||"number"!=typeof e[0][1])throw new Error("Input geometry is not a valid Polygon or MultiPolygon");var i=Ru.round(e[0][0],e[0][1]);this.bbox={ll:{x:i.x,y:i.y},ur:{x:i.x,y:i.y}};for(var o=i,s=1,a=e.length;sthis.bbox.ur.x&&(this.bbox.ur.x=u.x),u.y>this.bbox.ur.y&&(this.bbox.ur.y=u.y),o=u)}i.x===o.x&&i.y===o.y||this.segments.push(ju.fromRing(o,i,this))}return Iu(t,[{key:"getSweepEvents",value:function(){for(var t=[],e=0,n=this.segments.length;ethis.bbox.ur.x&&(this.bbox.ur.x=o.bbox.ur.x),o.bbox.ur.y>this.bbox.ur.y&&(this.bbox.ur.y=o.bbox.ur.y),this.interiorRings.push(o)}this.multiPoly=n}return Iu(t,[{key:"getSweepEvents",value:function(){for(var t=this.exteriorRing.getSweepEvents(),e=0,n=this.interiorRings.length;ethis.bbox.ur.x&&(this.bbox.ur.x=o.bbox.ur.x),o.bbox.ur.y>this.bbox.ur.y&&(this.bbox.ur.y=o.bbox.ur.y),this.polys.push(o)}this.isSubject=n}return Iu(t,[{key:"getSweepEvents",value:function(){for(var t=[],e=0,n=this.polys.length;e0&&(t=r)}for(var i=t.segment.prevInResult(),o=i?i.prevInResult():null;;){if(!i)return null;if(!o)return i.ringOut;if(o.ringOut!==i.ringOut)return o.ringOut.enclosingRing()!==i.ringOut?i.ringOut:i.ringOut.enclosingRing();i=o.prevInResult(),o=i?i.prevInResult():null}}}]),t}(),Hu=function(){function t(e){bu(this,t),this.exteriorRing=e,e.poly=this,this.interiorRings=[]}return Iu(t,[{key:"addInterior",value:function(t){this.interiorRings.push(t),t.poly=this}},{key:"getGeom",value:function(){var t=[this.exteriorRing.getGeom()];if(null===t[0])return null;for(var e=0,n=this.interiorRings.length;e1&&void 0!==arguments[1]?arguments[1]:ju.compare;bu(this,t),this.queue=e,this.tree=new _u(n),this.segments=[]}return Iu(t,[{key:"process",value:function(t){var e=t.segment,n=[];if(t.consumedBy)return t.isLeft?this.queue.remove(t.otherSE):this.tree.remove(e),n;var r=t.isLeft?this.tree.insert(e):this.tree.find(e);if(!r)throw new Error("Unable to find segment #".concat(e.id," ")+"[".concat(e.leftSE.point.x,", ").concat(e.leftSE.point.y,"] -> ")+"[".concat(e.rightSE.point.x,", ").concat(e.rightSE.point.y,"] ")+"in SweepLine tree. Please submit a bug report.");for(var i=r,o=r,s=void 0,a=void 0;void 0===s;)null===(i=this.tree.prev(i))?s=null:void 0===i.key.consumedBy&&(s=i.key);for(;void 0===a;)null===(o=this.tree.next(o))?a=null:void 0===o.key.consumedBy&&(a=o.key);if(t.isLeft){var u=null;if(s){var l=s.getIntersection(e);if(null!==l&&(e.isAnEndpoint(l)||(u=l),!s.isAnEndpoint(l)))for(var c=this._splitSafely(s,l),h=0,p=c.length;h0?(this.tree.remove(e),n.push(t)):(this.segments.push(e),e.prev=s)}else{if(s&&a){var b=s.getIntersection(a);if(null!==b){if(!s.isAnEndpoint(b))for(var w=this._splitSafely(s,b),I=0,N=w.length;IZu)throw new Error("Infinite loop when putting segment endpoints in a priority queue (queue size too big). Please file a bug report.");for(var m=new Ju(f),x=f.size,E=f.pop();E;){var b=E.key;if(f.size===x){var w=b.segment;throw new Error("Unable to pop() ".concat(b.isLeft?"left":"right"," SweepEvent ")+"[".concat(b.point.x,", ").concat(b.point.y,"] from segment #").concat(w.id," ")+"[".concat(w.leftSE.point.x,", ").concat(w.leftSE.point.y,"] -> ")+"[".concat(w.rightSE.point.x,", ").concat(w.rightSE.point.y,"] from queue. ")+"Please file a bug report.")}if(f.size>Zu)throw new Error("Infinite loop when passing sweep line over endpoints (queue size too big). Please file a bug report.");if(m.segments.length>Ku)throw new Error("Infinite loop when passing sweep line over endpoints (too many sweep line segments). Please file a bug report.");for(var I=m.process(b),N=0,S=I.length;N1?e-1:0),r=1;r1?e-1:0),r=1;r1?e-1:0),r=1;r1?e-1:0),r=1;re.x?1:this.ye.y?1:0},ul.prototype.clone=function(){},ul.prototype.copy=function(){return new ul(this)},ul.prototype.toString=function(){return"("+this.x+", "+this.y+", "+this.z+")"},ul.prototype.distance3D=function(t){var e=this.x-t.x,n=this.y-t.y,r=this.z-t.z;return Math.sqrt(e*e+n*n+r*r)},ul.prototype.distance=function(t){var e=this.x-t.x,n=this.y-t.y;return Math.sqrt(e*e+n*n)},ul.prototype.hashCode=function(){var t=17;return t=37*(t=37*t+ul.hashCode(this.x))+ul.hashCode(this.y)},ul.prototype.setCoordinate=function(t){this.x=t.x,this.y=t.y,this.z=t.z},ul.prototype.interfaces_=function(){return[il,ol,al]},ul.prototype.getClass=function(){return ul},ul.hashCode=function(){if(1===arguments.length){var t=arguments[0],e=nl.doubleToLongBits(t);return Math.trunc((e^e)>>>32)}},ll.DimensionalComparator.get=function(){return cl},ll.serialVersionUID.get=function(){return 0x5cbf2c235c7e5800},ll.NULL_ORDINATE.get=function(){return nl.NaN},ll.X.get=function(){return 0},ll.Y.get=function(){return 1},ll.Z.get=function(){return 2},Object.defineProperties(ul,ll);var cl=function(t){if(this._dimensionsToTest=2,0===arguments.length);else if(1===arguments.length){var e=arguments[0];if(2!==e&&3!==e)throw new el("only 2 or 3 dimensions may be specified");this._dimensionsToTest=e}};cl.prototype.compare=function(t,e){var n=t,r=e,i=cl.compare(n.x,r.x);if(0!==i)return i;var o=cl.compare(n.y,r.y);return 0!==o?o:this._dimensionsToTest<=2?0:cl.compare(n.z,r.z)},cl.prototype.interfaces_=function(){return[sl]},cl.prototype.getClass=function(){return cl},cl.compare=function(t,e){return te?1:nl.isNaN(t)?nl.isNaN(e)?0:-1:nl.isNaN(e)?1:0};var hl=function(){};hl.prototype.create=function(){},hl.prototype.interfaces_=function(){return[]},hl.prototype.getClass=function(){return hl};var pl=function(){},fl={INTERIOR:{configurable:!0},BOUNDARY:{configurable:!0},EXTERIOR:{configurable:!0},NONE:{configurable:!0}};pl.prototype.interfaces_=function(){return[]},pl.prototype.getClass=function(){return pl},pl.toLocationSymbol=function(t){switch(t){case pl.EXTERIOR:return"e";case pl.BOUNDARY:return"b";case pl.INTERIOR:return"i";case pl.NONE:return"-"}throw new el("Unknown location value: "+t)},fl.INTERIOR.get=function(){return 0},fl.BOUNDARY.get=function(){return 1},fl.EXTERIOR.get=function(){return 2},fl.NONE.get=function(){return-1},Object.defineProperties(pl,fl);var gl=function(t,e){return t.interfaces_&&t.interfaces_().indexOf(e)>-1},dl=function(){},yl={LOG_10:{configurable:!0}};dl.prototype.interfaces_=function(){return[]},dl.prototype.getClass=function(){return dl},dl.log10=function(t){var e=Math.log(t);return nl.isInfinite(e)||nl.isNaN(e)?e:e/dl.LOG_10},dl.min=function(t,e,n,r){var i=t;return en?n:t}if(Number.isInteger(arguments[2])&&Number.isInteger(arguments[0])&&Number.isInteger(arguments[1])){var r=arguments[0],i=arguments[1],o=arguments[2];return ro?o:r}},dl.wrap=function(t,e){return t<0?e- -t%e:t%e},dl.max=function(){if(3===arguments.length){var t=arguments[0],e=arguments[1],n=arguments[2],r=t;return e>r&&(r=e),n>r&&(r=n),r}if(4===arguments.length){var i=arguments[0],o=arguments[1],s=arguments[2],a=arguments[3],u=i;return o>u&&(u=o),s>u&&(u=s),a>u&&(u=a),u}},dl.average=function(t,e){return(t+e)/2},yl.LOG_10.get=function(){return Math.log(10)},Object.defineProperties(dl,yl);var vl=function(t){this.str=t};vl.prototype.append=function(t){this.str+=t},vl.prototype.setCharAt=function(t,e){this.str=this.str.substr(0,t)+e+this.str.substr(t+1)},vl.prototype.toString=function(t){return this.str};var _l=function(t){this.value=t};_l.prototype.intValue=function(){return this.value},_l.prototype.compareTo=function(t){return this.valuet?1:0},_l.isNaN=function(t){return Number.isNaN(t)};var ml=function(){};ml.isWhitespace=function(t){return t<=32&&t>=0||127===t},ml.toUpperCase=function(t){return t.toUpperCase()};var xl=function t(){if(this._hi=0,this._lo=0,0===arguments.length)this.init(0);else if(1===arguments.length){if("number"==typeof arguments[0]){var e=arguments[0];this.init(e)}else if(arguments[0]instanceof t){var n=arguments[0];this.init(n)}else if("string"==typeof arguments[0]){var r=arguments[0];t.call(this,t.parse(r))}}else if(2===arguments.length){var i=arguments[0],o=arguments[1];this.init(i,o)}},El={PI:{configurable:!0},TWO_PI:{configurable:!0},PI_2:{configurable:!0},E:{configurable:!0},NaN:{configurable:!0},EPS:{configurable:!0},SPLIT:{configurable:!0},MAX_PRINT_DIGITS:{configurable:!0},TEN:{configurable:!0},ONE:{configurable:!0},SCI_NOT_EXPONENT_CHAR:{configurable:!0},SCI_NOT_ZERO:{configurable:!0}};xl.prototype.le=function(t){return(this._hi9?(c=!0,h="9"):h="0"+l,s.append(h),n=n.subtract(xl.valueOf(l)).multiply(xl.TEN),c&&n.selfAdd(xl.TEN);var p=!0,f=xl.magnitude(n._hi);if(f<0&&Math.abs(f)>=a-u&&(p=!1),!p)break}return e[0]=r,s.toString()},xl.prototype.sqr=function(){return this.multiply(this)},xl.prototype.doubleValue=function(){return this._hi+this._lo},xl.prototype.subtract=function(){if(arguments[0]instanceof xl){var t=arguments[0];return this.add(t.negate())}if("number"==typeof arguments[0]){var e=arguments[0];return this.add(-e)}},xl.prototype.equals=function(){if(1===arguments.length){var t=arguments[0];return this._hi===t._hi&&this._lo===t._lo}},xl.prototype.isZero=function(){return 0===this._hi&&0===this._lo},xl.prototype.selfSubtract=function(){if(arguments[0]instanceof xl){var t=arguments[0];return this.isNaN()?this:this.selfAdd(-t._hi,-t._lo)}if("number"==typeof arguments[0]){var e=arguments[0];return this.isNaN()?this:this.selfAdd(-e,0)}},xl.prototype.getSpecialNumberString=function(){return this.isZero()?"0.0":this.isNaN()?"NaN ":null},xl.prototype.min=function(t){return this.le(t)?this:t},xl.prototype.selfDivide=function(){if(1===arguments.length){if(arguments[0]instanceof xl){var t=arguments[0];return this.selfDivide(t._hi,t._lo)}if("number"==typeof arguments[0]){var e=arguments[0];return this.selfDivide(e,0)}}else if(2===arguments.length){var n=arguments[0],r=arguments[1],i=null,o=null,s=null,a=null,u=null,l=null,c=null,h=null;return u=this._hi/n,h=(i=(l=xl.SPLIT*u)-(i=l-u))*(s=(h=xl.SPLIT*n)-(s=h-n))-(c=u*n)+i*(a=n-s)+(o=u-i)*s+o*a,h=u+(l=(this._hi-c-h+this._lo-u*r)/n),this._hi=h,this._lo=u-h+l,this}},xl.prototype.dump=function(){return"DD<"+this._hi+", "+this._lo+">"},xl.prototype.divide=function(){if(arguments[0]instanceof xl){var t=arguments[0],e=null,n=null,r=null,i=null,o=null,s=null,a=null,u=null;n=(o=this._hi/t._hi)-(e=(s=xl.SPLIT*o)-(e=s-o)),u=e*(r=(u=xl.SPLIT*t._hi)-(r=u-t._hi))-(a=o*t._hi)+e*(i=t._hi-r)+n*r+n*i;var l=u=o+(s=(this._hi-a-u+this._lo-o*t._lo)/t._hi),c=o-u+s;return new xl(l,c)}if("number"==typeof arguments[0]){var h=arguments[0];return nl.isNaN(h)?xl.createNaN():xl.copy(this).selfDivide(h,0)}},xl.prototype.ge=function(t){return(this._hi>t._hi||this._hi===t._hi)&&this._lo>=t._lo},xl.prototype.pow=function(t){if(0===t)return xl.valueOf(1);var e=new xl(this),n=xl.valueOf(1),r=Math.abs(t);if(r>1)for(;r>0;)r%2==1&&n.selfMultiply(e),(r/=2)>0&&(e=e.sqr());else n=e;return t<0?n.reciprocal():n},xl.prototype.ceil=function(){if(this.isNaN())return xl.NaN;var t=Math.ceil(this._hi),e=0;return t===this._hi&&(e=Math.ceil(this._lo)),new xl(t,e)},xl.prototype.compareTo=function(t){var e=t;return this._hie._hi?1:this._loe._lo?1:0},xl.prototype.rint=function(){return this.isNaN()?this:this.add(.5).floor()},xl.prototype.setValue=function(){if(arguments[0]instanceof xl){var t=arguments[0];return this.init(t),this}if("number"==typeof arguments[0]){var e=arguments[0];return this.init(e),this}},xl.prototype.max=function(t){return this.ge(t)?this:t},xl.prototype.sqrt=function(){if(this.isZero())return xl.valueOf(0);if(this.isNegative())return xl.NaN;var t=1/Math.sqrt(this._hi),e=this._hi*t,n=xl.valueOf(e),r=this.subtract(n.sqr())._hi*(.5*t);return n.add(r)},xl.prototype.selfAdd=function(){if(1===arguments.length){if(arguments[0]instanceof xl){var t=arguments[0];return this.selfAdd(t._hi,t._lo)}if("number"==typeof arguments[0]){var e=arguments[0],n=null,r=null,i=null,o=null,s=null,a=null;return o=(i=this._hi+e)-(s=i-this._hi),r=(a=(o=e-s+(this._hi-o))+this._lo)+(i-(n=i+a)),this._hi=n+r,this._lo=r+(n-this._hi),this}}else if(2===arguments.length){var u=arguments[0],l=arguments[1],c=null,h=null,p=null,f=null,g=null,d=null,y=null;f=this._hi+u,h=this._lo+l,g=f-(d=f-this._hi),p=h-(y=h-this._lo);var v=(c=f+(d=(g=u-d+(this._hi-g))+h))+(d=(p=l-y+(this._lo-p))+(d+(f-c))),_=d+(c-v);return this._hi=v,this._lo=_,this}},xl.prototype.selfMultiply=function(){if(1===arguments.length){if(arguments[0]instanceof xl){var t=arguments[0];return this.selfMultiply(t._hi,t._lo)}if("number"==typeof arguments[0]){var e=arguments[0];return this.selfMultiply(e,0)}}else if(2===arguments.length){var n=arguments[0],r=arguments[1],i=null,o=null,s=null,a=null,u=null,l=null;i=(u=xl.SPLIT*this._hi)-this._hi,l=xl.SPLIT*n,i=u-i,o=this._hi-i,s=l-n;var c=(u=this._hi*n)+(l=i*(s=l-s)-u+i*(a=n-s)+o*s+o*a+(this._hi*r+this._lo*n)),h=l+(i=u-c);return this._hi=c,this._lo=h,this}},xl.prototype.selfSqr=function(){return this.selfMultiply(this)},xl.prototype.floor=function(){if(this.isNaN())return xl.NaN;var t=Math.floor(this._hi),e=0;return t===this._hi&&(e=Math.floor(this._lo)),new xl(t,e)},xl.prototype.negate=function(){return this.isNaN()?this:new xl(-this._hi,-this._lo)},xl.prototype.clone=function(){},xl.prototype.multiply=function(){if(arguments[0]instanceof xl){var t=arguments[0];return t.isNaN()?xl.createNaN():xl.copy(this).selfMultiply(t)}if("number"==typeof arguments[0]){var e=arguments[0];return nl.isNaN(e)?xl.createNaN():xl.copy(this).selfMultiply(e,0)}},xl.prototype.isNaN=function(){return nl.isNaN(this._hi)},xl.prototype.intValue=function(){return Math.trunc(this._hi)},xl.prototype.toString=function(){var t=xl.magnitude(this._hi);return t>=-3&&t<=20?this.toStandardNotation():this.toSciNotation()},xl.prototype.toStandardNotation=function(){var t=this.getSpecialNumberString();if(null!==t)return t;var e=new Array(1).fill(null),n=this.extractSignificantDigits(!0,e),r=e[0]+1,i=n;if("."===n.charAt(0))i="0"+n;else if(r<0)i="0."+xl.stringOfChar("0",-r)+n;else if(-1===n.indexOf(".")){var o=r-n.length;i=n+xl.stringOfChar("0",o)+".0"}return this.isNegative()?"-"+i:i},xl.prototype.reciprocal=function(){var t,e,n,r,i=null,o=null,s=null,a=null;t=(n=1/this._hi)-(i=(s=xl.SPLIT*n)-(i=s-n)),o=(a=xl.SPLIT*this._hi)-this._hi;var u=n+(s=(1-(r=n*this._hi)-(a=i*(o=a-o)-r+i*(e=this._hi-o)+t*o+t*e)-n*this._lo)/this._hi);return new xl(u,n-u+s)},xl.prototype.toSciNotation=function(){if(this.isZero())return xl.SCI_NOT_ZERO;var t=this.getSpecialNumberString();if(null!==t)return t;var e=new Array(1).fill(null),n=this.extractSignificantDigits(!1,e),r=xl.SCI_NOT_EXPONENT_CHAR+e[0];if("0"===n.charAt(0))throw new Error("Found leading zero: "+n);var i="";n.length>1&&(i=n.substring(1));var o=n.charAt(0)+"."+i;return this.isNegative()?"-"+o+r:o+r},xl.prototype.abs=function(){return this.isNaN()?xl.NaN:this.isNegative()?this.negate():new xl(this)},xl.prototype.isPositive=function(){return(this._hi>0||0===this._hi)&&this._lo>0},xl.prototype.lt=function(t){return(this._hit._hi||this._hi===t._hi)&&this._lo>t._lo},xl.prototype.isNegative=function(){return(this._hi<0||0===this._hi)&&this._lo<0},xl.prototype.trunc=function(){return this.isNaN()?xl.NaN:this.isPositive()?this.floor():this.ceil()},xl.prototype.signum=function(){return this._hi>0?1:this._hi<0?-1:this._lo>0?1:this._lo<0?-1:0},xl.prototype.interfaces_=function(){return[al,il,ol]},xl.prototype.getClass=function(){return xl},xl.sqr=function(t){return xl.valueOf(t).selfMultiply(t)},xl.valueOf=function(){if("string"==typeof arguments[0]){var t=arguments[0];return xl.parse(t)}if("number"==typeof arguments[0]){var e=arguments[0];return new xl(e)}},xl.sqrt=function(t){return xl.valueOf(t).sqrt()},xl.parse=function(t){for(var e=0,n=t.length;ml.isWhitespace(t.charAt(e));)e++;var r=!1;if(e=n);){var l=t.charAt(e);if(e++,ml.isDigit(l)){var c=l-"0";o.selfMultiply(xl.TEN),o.selfAdd(c),s++}else{if("."!==l){if("e"===l||"E"===l){var h=t.substring(e);try{u=_l.parseInt(h)}catch(e){throw e instanceof Error?new Error("Invalid exponent "+h+" in string "+t):e}break}throw new Error("Unexpected character '"+l+"' at position "+e+" in string "+t)}a=s}}var p=o,f=s-a-u;if(0===f)p=o;else if(f>0){var g=xl.TEN.pow(f);p=o.divide(g)}else if(f<0){var d=xl.TEN.pow(-f);p=o.multiply(d)}return r?p.negate():p},xl.createNaN=function(){return new xl(nl.NaN,nl.NaN)},xl.copy=function(t){return new xl(t)},xl.magnitude=function(t){var e=Math.abs(t),n=Math.log(e)/Math.log(10),r=Math.trunc(Math.floor(n));return 10*Math.pow(10,r)<=e&&(r+=1),r},xl.stringOfChar=function(t,e){for(var n=new vl,r=0;r0){if(o<=0)return bl.signum(s);r=i+o}else{if(!(i<0))return bl.signum(s);if(o>=0)return bl.signum(s);r=-i-o}var a=bl.DP_SAFE_EPSILON*r;return s>=a||-s>=a?bl.signum(s):2},bl.signum=function(t){return t>0?1:t<0?-1:0},wl.DP_SAFE_EPSILON.get=function(){return 1e-15},Object.defineProperties(bl,wl);var Il=function(){},Nl={X:{configurable:!0},Y:{configurable:!0},Z:{configurable:!0},M:{configurable:!0}};Nl.X.get=function(){return 0},Nl.Y.get=function(){return 1},Nl.Z.get=function(){return 2},Nl.M.get=function(){return 3},Il.prototype.setOrdinate=function(t,e,n){},Il.prototype.size=function(){},Il.prototype.getOrdinate=function(t,e){},Il.prototype.getCoordinate=function(){},Il.prototype.getCoordinateCopy=function(t){},Il.prototype.getDimension=function(){},Il.prototype.getX=function(t){},Il.prototype.clone=function(){},Il.prototype.expandEnvelope=function(t){},Il.prototype.copy=function(){},Il.prototype.getY=function(t){},Il.prototype.toCoordinateArray=function(){},Il.prototype.interfaces_=function(){return[ol]},Il.prototype.getClass=function(){return Il},Object.defineProperties(Il,Nl);var Sl=function(){},Cl=function(t){function e(){t.call(this,"Projective point not representable on the Cartesian plane.")}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(Sl),Pl=function(){};Pl.arraycopy=function(t,e,n,r,i){for(var o=0,s=e;st._minx?this._minx:t._minx,n=this._miny>t._miny?this._miny:t._miny,r=this._maxx=this._minx&&e.getMaxX()<=this._maxx&&e.getMinY()>=this._miny&&e.getMaxY()<=this._maxy)}}else if(2===arguments.length){var n=arguments[0],r=arguments[1];return!this.isNull()&&(n>=this._minx&&n<=this._maxx&&r>=this._miny&&r<=this._maxy)}},Ll.prototype.intersects=function(){if(1===arguments.length){if(arguments[0]instanceof Ll){var t=arguments[0];return!this.isNull()&&!t.isNull()&&!(t._minx>this._maxx||t._maxxthis._maxy||t._maxythis._maxx||nthis._maxy||rthis._maxx&&(this._maxx=e._maxx),e._minythis._maxy&&(this._maxy=e._maxy))}}else if(2===arguments.length){var n=arguments[0],r=arguments[1];this.isNull()?(this._minx=n,this._maxx=n,this._miny=r,this._maxy=r):(nthis._maxx&&(this._maxx=n),rthis._maxy&&(this._maxy=r))}},Ll.prototype.minExtent=function(){if(this.isNull())return 0;var t=this.getWidth(),e=this.getHeight();return te._minx?1:this._minye._miny?1:this._maxxe._maxx?1:this._maxye._maxy?1:0},Ll.prototype.translate=function(t,e){if(this.isNull())return null;this.init(this.getMinX()+t,this.getMaxX()+t,this.getMinY()+e,this.getMaxY()+e)},Ll.prototype.toString=function(){return"Env["+this._minx+" : "+this._maxx+", "+this._miny+" : "+this._maxy+"]"},Ll.prototype.setToNull=function(){this._minx=0,this._maxx=-1,this._miny=0,this._maxy=-1},Ll.prototype.getHeight=function(){return this.isNull()?0:this._maxy-this._miny},Ll.prototype.maxExtent=function(){if(this.isNull())return 0;var t=this.getWidth(),e=this.getHeight();return t>e?t:e},Ll.prototype.expandBy=function(){if(1===arguments.length){var t=arguments[0];this.expandBy(t,t)}else if(2===arguments.length){var e=arguments[0],n=arguments[1];if(this.isNull())return null;this._minx-=e,this._maxx+=e,this._miny-=n,this._maxy+=n,(this._minx>this._maxx||this._miny>this._maxy)&&this.setToNull()}},Ll.prototype.contains=function(){if(1===arguments.length){if(arguments[0]instanceof Ll){var t=arguments[0];return this.covers(t)}if(arguments[0]instanceof ul){var e=arguments[0];return this.covers(e)}}else if(2===arguments.length){var n=arguments[0],r=arguments[1];return this.covers(n,r)}},Ll.prototype.centre=function(){return this.isNull()?null:new ul((this.getMinX()+this.getMaxX())/2,(this.getMinY()+this.getMaxY())/2)},Ll.prototype.init=function(){if(0===arguments.length)this.setToNull();else if(1===arguments.length){if(arguments[0]instanceof ul){var t=arguments[0];this.init(t.x,t.x,t.y,t.y)}else if(arguments[0]instanceof Ll){var e=arguments[0];this._minx=e._minx,this._maxx=e._maxx,this._miny=e._miny,this._maxy=e._maxy}}else if(2===arguments.length){var n=arguments[0],r=arguments[1];this.init(n.x,r.x,n.y,r.y)}else if(4===arguments.length){var i=arguments[0],o=arguments[1],s=arguments[2],a=arguments[3];it._maxx&&(e=this._minx-t._maxx);var n=0;return this._maxyt._maxy&&(n=this._miny-t._maxy),0===e?n:0===n?e:Math.sqrt(e*e+n*n)},Ll.prototype.hashCode=function(){var t=17;return t=37*(t=37*(t=37*(t=37*t+ul.hashCode(this._minx))+ul.hashCode(this._maxx))+ul.hashCode(this._miny))+ul.hashCode(this._maxy)},Ll.prototype.interfaces_=function(){return[il,al]},Ll.prototype.getClass=function(){return Ll},Ll.intersects=function(){if(3===arguments.length){var t=arguments[0],e=arguments[1],n=arguments[2];return n.x>=(t.xe.x?t.x:e.x)&&n.y>=(t.ye.y?t.y:e.y)}if(4===arguments.length){var r=arguments[0],i=arguments[1],o=arguments[2],s=arguments[3],a=Math.min(o.x,s.x),u=Math.max(o.x,s.x),l=Math.min(r.x,i.x),c=Math.max(r.x,i.x);return!(l>u)&&(!(cu)&&!(cn?(this._intLineIndex[t][0]=0,this._intLineIndex[t][1]=1):(this._intLineIndex[t][0]=1,this._intLineIndex[t][1]=0)}},Bl.prototype.isProper=function(){return this.hasIntersection()&&this._isProper},Bl.prototype.setPrecisionModel=function(t){this._precisionModel=t},Bl.prototype.isInteriorIntersection=function(){var t=this;if(0===arguments.length)return!!this.isInteriorIntersection(0)||!!this.isInteriorIntersection(1);if(1===arguments.length){for(var e=arguments[0],n=0;ni?r:i;else{var s=Math.abs(t.x-e.x),a=Math.abs(t.y-e.y);0!==(o=r>i?s:a)||t.equals(e)||(o=Math.max(s,a))}return ql.isTrue(!(0===o&&!t.equals(e)),"Bad distance calculation"),o},Bl.nonRobustComputeEdgeDistance=function(t,e,n){var r=t.x-e.x,i=t.y-e.y,o=Math.sqrt(r*r+i*i);return ql.isTrue(!(0===o&&!t.equals(e)),"Invalid distance calculation"),o},zl.DONT_INTERSECT.get=function(){return 0},zl.DO_INTERSECT.get=function(){return 1},zl.COLLINEAR.get=function(){return 2},zl.NO_INTERSECTION.get=function(){return 0},zl.POINT_INTERSECTION.get=function(){return 1},zl.COLLINEAR_INTERSECTION.get=function(){return 2},Object.defineProperties(Bl,zl);var jl=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.isInSegmentEnvelopes=function(t){var e=new Ll(this._inputLines[0][0],this._inputLines[0][1]),n=new Ll(this._inputLines[1][0],this._inputLines[1][1]);return e.contains(t)&&n.contains(t)},e.prototype.computeIntersection=function(){if(3!==arguments.length)return t.prototype.computeIntersection.apply(this,arguments);var e=arguments[0],n=arguments[1],r=arguments[2];if(this._isProper=!1,Ll.intersects(n,r,e)&&0===Xl.orientationIndex(n,r,e)&&0===Xl.orientationIndex(r,n,e))return this._isProper=!0,(e.equals(n)||e.equals(r))&&(this._isProper=!1),this._result=t.POINT_INTERSECTION,null;this._result=t.NO_INTERSECTION},e.prototype.normalizeToMinimum=function(t,e,n,r,i){i.x=this.smallestInAbsValue(t.x,e.x,n.x,r.x),i.y=this.smallestInAbsValue(t.y,e.y,n.y,r.y),t.x-=i.x,t.y-=i.y,e.x-=i.x,e.y-=i.y,n.x-=i.x,n.y-=i.y,r.x-=i.x,r.y-=i.y},e.prototype.safeHCoordinateIntersection=function(t,n,r,i){var o=null;try{o=Ml.intersection(t,n,r,i)}catch(s){if(!(s instanceof Cl))throw s;o=e.nearestEndpoint(t,n,r,i)}return o},e.prototype.intersection=function(t,n,r,i){var o=this.intersectionWithNormalization(t,n,r,i);return this.isInSegmentEnvelopes(o)||(o=new ul(e.nearestEndpoint(t,n,r,i))),null!==this._precisionModel&&this._precisionModel.makePrecise(o),o},e.prototype.smallestInAbsValue=function(t,e,n,r){var i=t,o=Math.abs(i);return Math.abs(e)1e-4&&Pl.out.println("Distance = "+i.distance(o))},e.prototype.intersectionWithNormalization=function(t,e,n,r){var i=new ul(t),o=new ul(e),s=new ul(n),a=new ul(r),u=new ul;this.normalizeToEnvCentre(i,o,s,a,u);var l=this.safeHCoordinateIntersection(i,o,s,a);return l.x+=u.x,l.y+=u.y,l},e.prototype.computeCollinearIntersection=function(e,n,r,i){var o=Ll.intersects(e,n,r),s=Ll.intersects(e,n,i),a=Ll.intersects(r,i,e),u=Ll.intersects(r,i,n);return o&&s?(this._intPt[0]=r,this._intPt[1]=i,t.COLLINEAR_INTERSECTION):a&&u?(this._intPt[0]=e,this._intPt[1]=n,t.COLLINEAR_INTERSECTION):o&&a?(this._intPt[0]=r,this._intPt[1]=e,!r.equals(e)||s||u?t.COLLINEAR_INTERSECTION:t.POINT_INTERSECTION):o&&u?(this._intPt[0]=r,this._intPt[1]=n,!r.equals(n)||s||a?t.COLLINEAR_INTERSECTION:t.POINT_INTERSECTION):s&&a?(this._intPt[0]=i,this._intPt[1]=e,!i.equals(e)||o||u?t.COLLINEAR_INTERSECTION:t.POINT_INTERSECTION):s&&u?(this._intPt[0]=i,this._intPt[1]=n,!i.equals(n)||o||a?t.COLLINEAR_INTERSECTION:t.POINT_INTERSECTION):t.NO_INTERSECTION},e.prototype.normalizeToEnvCentre=function(t,e,n,r,i){var o=t.xe.x?t.x:e.x,u=t.y>e.y?t.y:e.y,l=n.xr.x?n.x:r.x,p=n.y>r.y?n.y:r.y,f=((o>l?o:l)+(ac?s:c)+(u0&&s>0||o<0&&s<0)return t.NO_INTERSECTION;var a=Xl.orientationIndex(r,i,e),u=Xl.orientationIndex(r,i,n);return a>0&&u>0||a<0&&u<0?t.NO_INTERSECTION:0===o&&0===s&&0===a&&0===u?this.computeCollinearIntersection(e,n,r,i):(0===o||0===s||0===a||0===u?(this._isProper=!1,e.equals2D(r)||e.equals2D(i)?this._intPt[0]=e:n.equals2D(r)||n.equals2D(i)?this._intPt[0]=n:0===o?this._intPt[0]=new ul(r):0===s?this._intPt[0]=new ul(i):0===a?this._intPt[0]=new ul(e):0===u&&(this._intPt[0]=new ul(n))):(this._isProper=!0,this._intPt[0]=this.intersection(e,n,r,i)),t.POINT_INTERSECTION)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e.nearestEndpoint=function(t,e,n,r){var i=t,o=Xl.distancePointLine(t,n,r),s=Xl.distancePointLine(e,n,r);return s0?n>0?-i:i:n>0?i:-i;if(0===e||0===n)return r>0?t>0?i:-i:t>0?-i:i;if(e>0?r>0?e<=r||(i=-i,o=t,t=n,n=o,o=e,e=r,r=o):e<=-r?(i=-i,n=-n,r=-r):(o=t,t=-n,n=o,o=e,e=-r,r=o):r>0?-e<=r?(i=-i,t=-t,e=-e):(o=-t,t=n,n=o,o=-e,e=r,r=o):e>=r?(t=-t,e=-e,n=-n,r=-r):(i=-i,o=-t,t=-n,n=o,o=-e,e=-r,r=o),t>0){if(!(n>0))return i;if(!(t<=n))return i}else{if(n>0)return-i;if(!(t>=n))return-i;i=-i,t=-t,n=-n}for(;;){if((r-=(s=Math.floor(n/t))*e)<0)return-i;if(r>e)return i;if(t>(n-=s*t)+n){if(er+r)return-i;n=t-n,r=e-r,i=-i}if(0===r)return 0===n?0:-i;if(0===n)return i;if((e-=(s=Math.floor(t/n))*r)<0)return i;if(e>r)return-i;if(n>(t-=s*n)+t){if(re+e)return i;t=n-t,e=r-e,i=-i}if(0===e)return 0===t?0:i;if(0===t)return-i}};var Vl=function(){this._p=null,this._crossingCount=0,this._isPointOnSegment=!1;var t=arguments[0];this._p=t};Vl.prototype.countSegment=function(t,e){if(t.xr&&(n=e.x,r=t.x),this._p.x>=n&&this._p.x<=r&&(this._isPointOnSegment=!0),null}if(t.y>this._p.y&&e.y<=this._p.y||e.y>this._p.y&&t.y<=this._p.y){var i=t.x-this._p.x,o=t.y-this._p.y,s=e.x-this._p.x,a=e.y-this._p.y,u=Ul.signOfDet2x2(i,o,s,a);if(0===u)return this._isPointOnSegment=!0,null;a0&&this._crossingCount++}},Vl.prototype.isPointInPolygon=function(){return this.getLocation()!==pl.EXTERIOR},Vl.prototype.getLocation=function(){return this._isPointOnSegment?pl.BOUNDARY:this._crossingCount%2==1?pl.INTERIOR:pl.EXTERIOR},Vl.prototype.isOnSegment=function(){return this._isPointOnSegment},Vl.prototype.interfaces_=function(){return[]},Vl.prototype.getClass=function(){return Vl},Vl.locatePointInRing=function(){if(arguments[0]instanceof ul&&gl(arguments[1],Il)){for(var t=arguments[0],e=arguments[1],n=new Vl(t),r=new ul,i=new ul,o=1;o1||a<0||a>1)&&(i=!0)}}else i=!0;return i?dl.min(Xl.distancePointLine(t,n,r),Xl.distancePointLine(e,n,r),Xl.distancePointLine(n,t,e),Xl.distancePointLine(r,t,e)):0},Xl.isPointInRing=function(t,e){return Xl.locatePointInRing(t,e)!==pl.EXTERIOR},Xl.computeLength=function(t){var e=t.size();if(e<=1)return 0;var n=0,r=new ul;t.getCoordinate(0,r);for(var i=r.x,o=r.y,s=1;sn.y&&(n=o,r=i)}var s=r;do{(s-=1)<0&&(s=e)}while(t[s].equals2D(n)&&s!==r);var a=r;do{a=(a+1)%e}while(t[a].equals2D(n)&&a!==r);var u=t[s],l=t[a];if(u.equals2D(n)||l.equals2D(n)||u.equals2D(l))return!1;var c=Xl.computeOrientation(u,n,l),h=!1;return h=0===c?u.x>l.x:c>0,h},Xl.locatePointInRing=function(t,e){return Vl.locatePointInRing(t,e)},Xl.distancePointLinePerpendicular=function(t,e,n){var r=(n.x-e.x)*(n.x-e.x)+(n.y-e.y)*(n.y-e.y),i=((e.y-t.y)*(n.x-e.x)-(e.x-t.x)*(n.y-e.y))/r;return Math.abs(i)*Math.sqrt(r)},Xl.computeOrientation=function(t,e,n){return Xl.orientationIndex(t,e,n)},Xl.distancePointLine=function(){if(2===arguments.length){var t=arguments[0],e=arguments[1];if(0===e.length)throw new el("Line array must contain at least one vertex");for(var n=t.distance(e[0]),r=0;r=1)return o.distance(a);var c=((s.y-o.y)*(a.x-s.x)-(s.x-o.x)*(a.y-s.y))/u;return Math.abs(c)*Math.sqrt(u)}},Xl.isOnLine=function(t,e){for(var n=new jl,r=1;r0},ec.prototype.interfaces_=function(){return[Ql]},ec.prototype.getClass=function(){return ec};var nc=function(){};nc.prototype.isInBoundary=function(t){return t>1},nc.prototype.interfaces_=function(){return[Ql]},nc.prototype.getClass=function(){return nc};var rc=function(){};rc.prototype.isInBoundary=function(t){return 1===t},rc.prototype.interfaces_=function(){return[Ql]},rc.prototype.getClass=function(){return rc};var ic=function(){};function oc(t){this.message=t||""}ic.prototype.add=function(){},ic.prototype.addAll=function(){},ic.prototype.isEmpty=function(){},ic.prototype.iterator=function(){},ic.prototype.size=function(){},ic.prototype.toArray=function(){},ic.prototype.remove=function(){},oc.prototype=new Error,oc.prototype.name="IndexOutOfBoundsException";var sc=function(){};sc.prototype.hasNext=function(){},sc.prototype.next=function(){},sc.prototype.remove=function(){};var ac=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.get=function(){},e.prototype.set=function(){},e.prototype.isEmpty=function(){},e}(ic);function uc(t){this.message=t||""}uc.prototype=new Error,uc.prototype.name="NoSuchElementException";var lc=function(t){function e(){t.call(this),this.array_=[],arguments[0]instanceof ic&&this.addAll(arguments[0])}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.ensureCapacity=function(){},e.prototype.interfaces_=function(){return[t,ic]},e.prototype.add=function(t){return 1===arguments.length?this.array_.push(t):this.array_.splice(arguments[0],arguments[1]),!0},e.prototype.clear=function(){this.array_=[]},e.prototype.addAll=function(t){for(var e=t.iterator();e.hasNext();)this.add(e.next());return!0},e.prototype.set=function(t,e){var n=this.array_[t];return this.array_[t]=e,n},e.prototype.iterator=function(){return new cc(this)},e.prototype.get=function(t){if(t<0||t>=this.size())throw new oc;return this.array_[t]},e.prototype.isEmpty=function(){return 0===this.array_.length},e.prototype.size=function(){return this.array_.length},e.prototype.toArray=function(){for(var t=[],e=0,n=this.array_.length;e=1){var a=this.get(this.size()-1);if(a.equals2D(o))return null}t.prototype.add.call(this,o)}else if(arguments[0]instanceof Object&&"boolean"==typeof arguments[1]){var u=arguments[0],l=arguments[1];return this.add(u,l),!0}}else if(3===arguments.length){if("boolean"==typeof arguments[2]&&arguments[0]instanceof Array&&"boolean"==typeof arguments[1]){var c=arguments[0],h=arguments[1],p=arguments[2];if(p)for(var f=0;f=0;g--)e.add(c[g],h);return!0}if("boolean"==typeof arguments[2]&&Number.isInteger(arguments[0])&&arguments[1]instanceof ul){var d=arguments[0],y=arguments[1],v=arguments[2];if(!v){var _=this.size();if(_>0){if(d>0){var m=this.get(d-1);if(m.equals2D(y))return null}if(d<_){var x=this.get(d);if(x.equals2D(y))return null}}}t.prototype.add.call(this,d,y)}}else if(4===arguments.length){var E=arguments[0],b=arguments[1],w=arguments[2],I=arguments[3],N=1;w>I&&(N=-1);for(var S=w;S!==I;S+=N)e.add(E[S],b);return!0}},e.prototype.closeRing=function(){this.size()>0&&this.add(new ul(this.get(0)),!1)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},Object.defineProperties(e,n),e}(lc),pc=function(){},fc={ForwardComparator:{configurable:!0},BidirectionalComparator:{configurable:!0},coordArrayType:{configurable:!0}};fc.ForwardComparator.get=function(){return gc},fc.BidirectionalComparator.get=function(){return dc},fc.coordArrayType.get=function(){return new Array(0).fill(null)},pc.prototype.interfaces_=function(){return[]},pc.prototype.getClass=function(){return pc},pc.isRing=function(t){return!(t.length<4)&&!!t[0].equals2D(t[t.length-1])},pc.ptNotInList=function(t,e){for(var n=0;n=t?e:[]},pc.indexOf=function(t,e){for(var n=0;n0)&&(e=t[n]);return e},pc.extract=function(t,e,n){e=dl.clamp(e,0,t.length);var r=(n=dl.clamp(n,-1,t.length))-e+1;n<0&&(r=0),e>=t.length&&(r=0),nr.length)return 1;if(0===n.length)return 0;var i=pc.compare(n,r);return pc.isEqualReversed(n,r)?0:i},dc.prototype.OLDcompare=function(t,e){var n=t,r=e;if(n.lengthr.length)return 1;if(0===n.length)return 0;for(var i=pc.increasingDirection(n),o=pc.increasingDirection(r),s=i>0?0:n.length-1,a=o>0?0:n.length-1,u=0;u0))return e.value;e=e.right}}return null},Cc.prototype.put=function(t,e){if(null===this.root_)return this.root_={key:t,value:e,left:null,right:null,parent:null,color:0,getValue:function(){return this.value},getKey:function(){return this.key}},this.size_=1,null;var n,r,i=this.root_;do{if(n=i,(r=t.compareTo(i.key))<0)i=i.left;else{if(!(r>0)){var o=i.value;return i.value=e,o}i=i.right}}while(null!==i);var s={key:t,left:null,right:null,value:e,parent:n,color:0,getValue:function(){return this.value},getKey:function(){return this.key}};return r<0?n.left=s:n.right=s,this.fixAfterInsertion(s),this.size_++,null},Cc.prototype.fixAfterInsertion=function(t){var e=this;for(t.color=1;null!=t&&t!==this.root_&&1===t.parent.color;)if(wc(t)===Nc(wc(wc(t)))){var n=Sc(wc(wc(t)));1===bc(n)?(Ic(wc(t),0),Ic(n,0),Ic(wc(wc(t)),1),t=wc(wc(t))):(t===Sc(wc(t))&&(t=wc(t),e.rotateLeft(t)),Ic(wc(t),0),Ic(wc(wc(t)),1),e.rotateRight(wc(wc(t))))}else{var r=Nc(wc(wc(t)));1===bc(r)?(Ic(wc(t),0),Ic(r,0),Ic(wc(wc(t)),1),t=wc(wc(t))):(t===Nc(wc(t))&&(t=wc(t),e.rotateRight(t)),Ic(wc(t),0),Ic(wc(wc(t)),1),e.rotateLeft(wc(wc(t))))}this.root_.color=0},Cc.prototype.values=function(){var t=new lc,e=this.getFirstEntry();if(null!==e)for(t.add(e.value);null!==(e=Cc.successor(e));)t.add(e.value);return t},Cc.prototype.entrySet=function(){var t=new xc,e=this.getFirstEntry();if(null!==e)for(t.add(e);null!==(e=Cc.successor(e));)t.add(e);return t},Cc.prototype.rotateLeft=function(t){if(null!=t){var e=t.right;t.right=e.left,null!=e.left&&(e.left.parent=t),e.parent=t.parent,null===t.parent?this.root_=e:t.parent.left===t?t.parent.left=e:t.parent.right=e,e.left=t,t.parent=e}},Cc.prototype.rotateRight=function(t){if(null!=t){var e=t.left;t.left=e.right,null!=e.right&&(e.right.parent=t),e.parent=t.parent,null===t.parent?this.root_=e:t.parent.right===t?t.parent.right=e:t.parent.left=e,e.right=t,t.parent=e}},Cc.prototype.getFirstEntry=function(){var t=this.root_;if(null!=t)for(;null!=t.left;)t=t.left;return t},Cc.successor=function(t){if(null===t)return null;if(null!==t.right){for(var e=t.right;null!==e.left;)e=e.left;return e}for(var n=t.parent,r=t;null!==n&&r===n.right;)r=n,n=n.parent;return n},Cc.prototype.size=function(){return this.size_};var Pc=function(){};function Mc(){}function Lc(){this.array_=[],arguments[0]instanceof ic&&this.addAll(arguments[0])}Pc.prototype.interfaces_=function(){return[]},Pc.prototype.getClass=function(){return Pc},Mc.prototype=new mc,Lc.prototype=new Mc,Lc.prototype.contains=function(t){for(var e=0,n=this.array_.length;e=0;){var s=i.substring(0,o);r.add(s),o=(i=i.substring(o+n)).indexOf(e)}i.length>0&&r.add(i);for(var a=new Array(r.size()).fill(null),u=0;u0)for(var o=i;o0&&r.append(" ");for(var o=0;o0&&r.append(","),r.append(Hc.toString(t.getOrdinate(i,o)))}return r.append(")"),r.toString()}},Jc.ensureValidRing=function(t,e){var n=e.size();return 0===n?e:n<=3?Jc.createClosedRing(t,e,4):e.getOrdinate(0,Il.X)===e.getOrdinate(n-1,Il.X)&&e.getOrdinate(0,Il.Y)===e.getOrdinate(n-1,Il.Y)?e:Jc.createClosedRing(t,e,n+1)},Jc.createClosedRing=function(t,e,n){var r=t.create(n,e.getDimension()),i=e.size();Jc.copy(e,0,r,0,i);for(var o=i;o0&&Jc.reverse(t._points),null}},e.prototype.getCoordinate=function(){return this.isEmpty()?null:this._points.getCoordinate(0)},e.prototype.getBoundaryDimension=function(){return this.isClosed()?Tc.FALSE:0},e.prototype.isClosed=function(){return!this.isEmpty()&&this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints()-1))},e.prototype.getEndPoint=function(){return this.isEmpty()?null:this.getPointN(this.getNumPoints()-1)},e.prototype.getDimension=function(){return 1},e.prototype.getLength=function(){return Xl.computeLength(this._points)},e.prototype.getNumPoints=function(){return this._points.size()},e.prototype.reverse=function(){var t=this._points.copy();return Jc.reverse(t),this.getFactory().createLineString(t)},e.prototype.compareToSameClass=function(){var t=this;if(1===arguments.length){for(var e=arguments[0],n=e,r=0,i=0;r= 2)");this._points=t},e.prototype.isCoordinate=function(t){for(var e=0;e=1&&this.getCoordinateSequence().size()= 4)")},e.prototype.getGeometryType=function(){return"LinearRing"},e.prototype.copy=function(){return new e(this._points.copy(),this._factory)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},n.MINIMUM_VALID_SIZE.get=function(){return 4},n.serialVersionUID.get=function(){return-0x3b229e262367a600},Object.defineProperties(e,n),e}(Zc),rh=function(t){function e(){t.apply(this,arguments)}t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e;var n={serialVersionUID:{configurable:!0}};return e.prototype.getSortIndex=function(){return Wl.SORTINDEX_MULTIPOLYGON},e.prototype.equalsExact=function(){if(2===arguments.length){var e=arguments[0],n=arguments[1];return!!this.isEquivalentClass(e)&&t.prototype.equalsExact.call(this,e,n)}return t.prototype.equalsExact.apply(this,arguments)},e.prototype.getBoundaryDimension=function(){return 1},e.prototype.getDimension=function(){return 2},e.prototype.reverse=function(){for(var t=this._geometries.length,e=new Array(t).fill(null),n=0;n0?e.createPoint(n[0]):e.createPoint():t},ah.prototype.interfaces_=function(){return[ih.GeometryEditorOperation]},ah.prototype.getClass=function(){return ah};var uh=function(){};uh.prototype.edit=function(t,e){return t instanceof nh?e.createLinearRing(this.edit(t.getCoordinateSequence(),t)):t instanceof Zc?e.createLineString(this.edit(t.getCoordinateSequence(),t)):t instanceof Qc?e.createPoint(this.edit(t.getCoordinateSequence(),t)):t},uh.prototype.interfaces_=function(){return[ih.GeometryEditorOperation]},uh.prototype.getClass=function(){return uh};var lh=function(){var t=this;if(this._dimension=3,this._coordinates=null,1===arguments.length){if(arguments[0]instanceof Array)this._coordinates=arguments[0],this._dimension=3;else if(Number.isInteger(arguments[0])){var e=arguments[0];this._coordinates=new Array(e).fill(null);for(var n=0;n0){var t=new vl(17*this._coordinates.length);t.append("("),t.append(this._coordinates[0]);for(var e=1;e3&&(r=3),r<2?new lh(n):new lh(n,r)}},hh.prototype.interfaces_=function(){return[hl,al]},hh.prototype.getClass=function(){return hh},hh.instance=function(){return hh.instanceObject},ph.serialVersionUID.get=function(){return-0x38e49fa6cf6f2e00},ph.instanceObject.get=function(){return new hh},Object.defineProperties(hh,ph);var fh=function(t){function e(){t.call(this),this.map_=new Map}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.get=function(t){return this.map_.get(t)||null},e.prototype.put=function(t,e){return this.map_.set(t,e),e},e.prototype.values=function(){for(var t=new lc,e=this.map_.values(),n=e.next();!n.done;)t.add(n.value),n=e.next();return t},e.prototype.entrySet=function(){var t=new xc;return this.map_.entries().forEach((function(e){return t.add(e)})),t},e.prototype.size=function(){return this.map_.size()},e}(yc),gh=function t(){if(this._modelType=null,this._scale=null,0===arguments.length)this._modelType=t.FLOATING;else if(1===arguments.length)if(arguments[0]instanceof yh){var e=arguments[0];this._modelType=e,e===t.FIXED&&this.setScale(1)}else if("number"==typeof arguments[0]){var n=arguments[0];this._modelType=t.FIXED,this.setScale(n)}else if(arguments[0]instanceof t){var r=arguments[0];this._modelType=r._modelType,this._scale=r._scale}},dh={serialVersionUID:{configurable:!0},maximumPreciseValue:{configurable:!0}};gh.prototype.equals=function(t){if(!(t instanceof gh))return!1;var e=t;return this._modelType===e._modelType&&this._scale===e._scale},gh.prototype.compareTo=function(t){var e=t,n=this.getMaximumSignificantDigits(),r=e.getMaximumSignificantDigits();return new _l(n).compareTo(new _l(r))},gh.prototype.getScale=function(){return this._scale},gh.prototype.isFloating=function(){return this._modelType===gh.FLOATING||this._modelType===gh.FLOATING_SINGLE},gh.prototype.getType=function(){return this._modelType},gh.prototype.toString=function(){var t="UNKNOWN";return this._modelType===gh.FLOATING?t="Floating":this._modelType===gh.FLOATING_SINGLE?t="Floating-Single":this._modelType===gh.FIXED&&(t="Fixed (Scale="+this.getScale()+")"),t},gh.prototype.makePrecise=function(){if("number"==typeof arguments[0]){var t=arguments[0];if(nl.isNaN(t))return t;if(this._modelType===gh.FLOATING_SINGLE){return t}return this._modelType===gh.FIXED?Math.round(t*this._scale)/this._scale:t}if(arguments[0]instanceof ul){var e=arguments[0];if(this._modelType===gh.FLOATING)return null;e.x=this.makePrecise(e.x),e.y=this.makePrecise(e.y)}},gh.prototype.getMaximumSignificantDigits=function(){var t=16;return this._modelType===gh.FLOATING?t=16:this._modelType===gh.FLOATING_SINGLE?t=6:this._modelType===gh.FIXED&&(t=1+Math.trunc(Math.ceil(Math.log(this.getScale())/Math.log(10)))),t},gh.prototype.setScale=function(t){this._scale=Math.abs(t)},gh.prototype.interfaces_=function(){return[al,il]},gh.prototype.getClass=function(){return gh},gh.mostPrecise=function(t,e){return t.compareTo(e)>=0?t:e},dh.serialVersionUID.get=function(){return 0x6bee6404e9a25c00},dh.maximumPreciseValue.get=function(){return 9007199254740992},Object.defineProperties(gh,dh);var yh=function t(e){this._name=e||null,t.nameToTypeMap.put(e,this)},vh={serialVersionUID:{configurable:!0},nameToTypeMap:{configurable:!0}};yh.prototype.readResolve=function(){return yh.nameToTypeMap.get(this._name)},yh.prototype.toString=function(){return this._name},yh.prototype.interfaces_=function(){return[al]},yh.prototype.getClass=function(){return yh},vh.serialVersionUID.get=function(){return-552860263173159e4},vh.nameToTypeMap.get=function(){return new fh},Object.defineProperties(yh,vh),gh.Type=yh,gh.FIXED=new yh("FIXED"),gh.FLOATING=new yh("FLOATING"),gh.FLOATING_SINGLE=new yh("FLOATING SINGLE");var _h=function t(){this._precisionModel=new gh,this._SRID=0,this._coordinateSequenceFactory=t.getDefaultCoordinateSequenceFactory(),0===arguments.length||(1===arguments.length?gl(arguments[0],hl)?this._coordinateSequenceFactory=arguments[0]:arguments[0]instanceof gh&&(this._precisionModel=arguments[0]):2===arguments.length?(this._precisionModel=arguments[0],this._SRID=arguments[1]):3===arguments.length&&(this._precisionModel=arguments[0],this._SRID=arguments[1],this._coordinateSequenceFactory=arguments[2]))},mh={serialVersionUID:{configurable:!0}};_h.prototype.toGeometry=function(t){return t.isNull()?this.createPoint(null):t.getMinX()===t.getMaxX()&&t.getMinY()===t.getMaxY()?this.createPoint(new ul(t.getMinX(),t.getMinY())):t.getMinX()===t.getMaxX()||t.getMinY()===t.getMaxY()?this.createLineString([new ul(t.getMinX(),t.getMinY()),new ul(t.getMaxX(),t.getMaxY())]):this.createPolygon(this.createLinearRing([new ul(t.getMinX(),t.getMinY()),new ul(t.getMinX(),t.getMaxY()),new ul(t.getMaxX(),t.getMaxY()),new ul(t.getMaxX(),t.getMinY()),new ul(t.getMinX(),t.getMinY())]),null)},_h.prototype.createLineString=function(t){return t?t instanceof Array?new Zc(this.getCoordinateSequenceFactory().create(t),this):gl(t,Il)?new Zc(t,this):void 0:new Zc(this.getCoordinateSequenceFactory().create([]),this)},_h.prototype.createMultiLineString=function(){if(0===arguments.length)return new Gc(null,this);if(1===arguments.length){var t=arguments[0];return new Gc(t,this)}},_h.prototype.buildGeometry=function(t){for(var e=null,n=!1,r=!1,i=t.iterator();i.hasNext();){var o=i.next(),s=o.getClass();null===e&&(e=s),s!==e&&(n=!0),o.isGeometryCollectionOrDerived()&&(r=!0)}if(null===e)return this.createGeometryCollection();if(n||r)return this.createGeometryCollection(_h.toGeometryArray(t));var a=t.iterator().next();if(t.size()>1){if(a instanceof th)return this.createMultiPolygon(_h.toPolygonArray(t));if(a instanceof Zc)return this.createMultiLineString(_h.toLineStringArray(t));if(a instanceof Qc)return this.createMultiPoint(_h.toPointArray(t));ql.shouldNeverReachHere("Unhandled class: "+a.getClass().getName())}return a},_h.prototype.createMultiPointFromCoords=function(t){return this.createMultiPoint(null!==t?this.getCoordinateSequenceFactory().create(t):null)},_h.prototype.createPoint=function(){if(0===arguments.length)return this.createPoint(this.getCoordinateSequenceFactory().create([]));if(1===arguments.length){if(arguments[0]instanceof ul){var t=arguments[0];return this.createPoint(null!==t?this.getCoordinateSequenceFactory().create([t]):null)}if(gl(arguments[0],Il)){var e=arguments[0];return new Qc(e,this)}}},_h.prototype.getCoordinateSequenceFactory=function(){return this._coordinateSequenceFactory},_h.prototype.createPolygon=function(){if(0===arguments.length)return new th(null,null,this);if(1===arguments.length){if(gl(arguments[0],Il)){var t=arguments[0];return this.createPolygon(this.createLinearRing(t))}if(arguments[0]instanceof Array){var e=arguments[0];return this.createPolygon(this.createLinearRing(e))}if(arguments[0]instanceof nh){var n=arguments[0];return this.createPolygon(n,null)}}else if(2===arguments.length){var r=arguments[0],i=arguments[1];return new th(r,i,this)}},_h.prototype.getSRID=function(){return this._SRID},_h.prototype.createGeometryCollection=function(){if(0===arguments.length)return new kc(null,this);if(1===arguments.length){var t=arguments[0];return new kc(t,this)}},_h.prototype.createGeometry=function(t){return new ih(this).edit(t,{edit:function(){if(2===arguments.length){var t=arguments[0];return this._coordinateSequenceFactory.create(t)}}})},_h.prototype.getPrecisionModel=function(){return this._precisionModel},_h.prototype.createLinearRing=function(){if(0===arguments.length)return this.createLinearRing(this.getCoordinateSequenceFactory().create([]));if(1===arguments.length){if(arguments[0]instanceof Array){var t=arguments[0];return this.createLinearRing(null!==t?this.getCoordinateSequenceFactory().create(t):null)}if(gl(arguments[0],Il)){var e=arguments[0];return new nh(e,this)}}},_h.prototype.createMultiPolygon=function(){if(0===arguments.length)return new rh(null,this);if(1===arguments.length){var t=arguments[0];return new rh(t,this)}},_h.prototype.createMultiPoint=function(){var t=this;if(0===arguments.length)return new eh(null,this);if(1===arguments.length){if(arguments[0]instanceof Array){var e=arguments[0];return new eh(e,this)}if(arguments[0]instanceof Array){var n=arguments[0];return this.createMultiPoint(null!==n?this.getCoordinateSequenceFactory().create(n):null)}if(gl(arguments[0],Il)){var r=arguments[0];if(null===r)return this.createMultiPoint(new Array(0).fill(null));for(var i=new Array(r.size()).fill(null),o=0;o=this.size())throw new Error;return this.array_[t]},Mh.prototype.push=function(t){return this.array_.push(t),t},Mh.prototype.pop=function(t){if(0===this.array_.length)throw new Ph;return this.array_.pop()},Mh.prototype.peek=function(){if(0===this.array_.length)throw new Ph;return this.array_[this.array_.length-1]},Mh.prototype.empty=function(){return 0===this.array_.length},Mh.prototype.isEmpty=function(){return this.empty()},Mh.prototype.search=function(t){return this.array_.indexOf(t)},Mh.prototype.size=function(){return this.array_.length},Mh.prototype.toArray=function(){for(var t=[],e=0,n=this.array_.length;e0&&this._minIndexthis._minCoord.y&&n.y>this._minCoord.y&&r===Xl.CLOCKWISE)&&(i=!0),i&&(this._minIndex=this._minIndex-1)},Lh.prototype.getRightmostSideOfSegment=function(t,e){var n=t.getEdge().getCoordinates();if(e<0||e+1>=n.length)return-1;if(n[e].y===n[e+1].y)return-1;var r=Sh.LEFT;return n[e].ye._minCoord.x)&&(e._minDe=t,e._minIndex=r,e._minCoord=n[r])},Lh.prototype.findRightmostEdgeAtNode=function(){var t=this._minDe.getNode().getEdges();this._minDe=t.getRightmostEdge(),this._minDe.isForward()||(this._minDe=this._minDe.getSym(),this._minIndex=this._minDe.getEdge().getCoordinates().length-1)},Lh.prototype.findEdge=function(t){for(var e=t.iterator();e.hasNext();){var n=e.next();n.isForward()&&this.checkForRightmostCoordinate(n)}ql.isTrue(0!==this._minIndex||this._minCoord.equals(this._minDe.getCoordinate()),"inconsistency in rightmost processing"),0===this._minIndex?this.findRightmostEdgeAtNode():this.findRightmostEdgeAtVertex(),this._orientedDe=this._minDe,this.getRightmostSide(this._minDe,this._minIndex)===Sh.LEFT&&(this._orientedDe=this._minDe.getSym())},Lh.prototype.interfaces_=function(){return[]},Lh.prototype.getClass=function(){return Lh};var Oh=function(t){function e(n,r){t.call(this,e.msgWithCoord(n,r)),this.pt=r?new ul(r):null,this.name="TopologyException"}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.getCoordinate=function(){return this.pt},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e.msgWithCoord=function(t,e){return e?t:t+" [ "+e+" ]"},e}(kl),Rh=function(){this.array_=[]};Rh.prototype.addLast=function(t){this.array_.push(t)},Rh.prototype.removeFirst=function(){return this.array_.shift()},Rh.prototype.isEmpty=function(){return 0===this.array_.length};var Th=function(){this._finder=null,this._dirEdgeList=new lc,this._nodes=new lc,this._rightMostCoord=null,this._env=null,this._finder=new Lh};Th.prototype.clearVisitedEdges=function(){for(var t=this._dirEdgeList.iterator();t.hasNext();){t.next().setVisited(!1)}},Th.prototype.getRightmostCoordinate=function(){return this._rightMostCoord},Th.prototype.computeNodeDepth=function(t){for(var e=null,n=t.getEdges().iterator();n.hasNext();){var r=n.next();if(r.isVisited()||r.getSym().isVisited()){e=r;break}}if(null===e)throw new Oh("unable to find edge to compute depths at "+t.getCoordinate());t.getEdges().computeDepths(e);for(var i=t.getEdges().iterator();i.hasNext();){var o=i.next();o.setVisited(!0),this.copySymDepths(o)}},Th.prototype.computeDepth=function(t){this.clearVisitedEdges();var e=this._finder.getEdge();e.setEdgeDepths(Sh.RIGHT,t),this.copySymDepths(e),this.computeDepths(e)},Th.prototype.create=function(t){this.addReachable(t),this._finder.findEdge(this._dirEdgeList),this._rightMostCoord=this._finder.getCoordinate()},Th.prototype.findResultEdges=function(){for(var t=this._dirEdgeList.iterator();t.hasNext();){var e=t.next();e.getDepth(Sh.RIGHT)>=1&&e.getDepth(Sh.LEFT)<=0&&!e.isInteriorAreaEdge()&&e.setInResult(!0)}},Th.prototype.computeDepths=function(t){var e=new xc,n=new Rh,r=t.getNode();for(n.addLast(r),e.add(r),t.setVisited(!0);!n.isEmpty();){var i=n.removeFirst();e.add(i),this.computeNodeDepth(i);for(var o=i.getEdges().iterator();o.hasNext();){var s=o.next().getSym();if(!s.isVisited()){var a=s.getNode();e.contains(a)||(n.addLast(a),e.add(a))}}}},Th.prototype.compareTo=function(t){var e=t;return this._rightMostCoord.xe._rightMostCoord.x?1:0},Th.prototype.getEnvelope=function(){if(null===this._env){for(var t=new Ll,e=this._dirEdgeList.iterator();e.hasNext();)for(var n=e.next().getEdge().getCoordinates(),r=0;rthis.location.length){var e=new Array(3).fill(null);e[Sh.ON]=this.location[Sh.ON],e[Sh.LEFT]=pl.NONE,e[Sh.RIGHT]=pl.NONE,this.location=e}for(var n=0;n1&&t.append(pl.toLocationSymbol(this.location[Sh.LEFT])),t.append(pl.toLocationSymbol(this.location[Sh.ON])),this.location.length>1&&t.append(pl.toLocationSymbol(this.location[Sh.RIGHT])),t.toString()},Ah.prototype.setLocations=function(t,e,n){this.location[Sh.ON]=t,this.location[Sh.LEFT]=e,this.location[Sh.RIGHT]=n},Ah.prototype.get=function(t){return t1},Ah.prototype.isAnyNull=function(){for(var t=0;tt._maxNodeDegree&&(t._maxNodeDegree=n),e=t.getNext(e)}while(e!==this._startDe);this._maxNodeDegree*=2},Fh.prototype.addPoints=function(t,e,n){var r=t.getCoordinates();if(e){var i=1;n&&(i=0);for(var o=i;o=0;a--)this._pts.add(r[a])}},Fh.prototype.isHole=function(){return this._isHole},Fh.prototype.setInResult=function(){var t=this._startDe;do{t.getEdge().setInResult(!0),t=t.getNext()}while(t!==this._startDe)},Fh.prototype.containsPoint=function(t){var e=this.getLinearRing();if(!e.getEnvelopeInternal().contains(t))return!1;if(!Xl.isPointInRing(t,e.getCoordinates()))return!1;for(var n=this._holes.iterator();n.hasNext();){if(n.next().containsPoint(t))return!1}return!0},Fh.prototype.addHole=function(t){this._holes.add(t)},Fh.prototype.isShell=function(){return null===this._shell},Fh.prototype.getLabel=function(){return this._label},Fh.prototype.getEdges=function(){return this._edges},Fh.prototype.getMaxNodeDegree=function(){return this._maxNodeDegree<0&&this.computeMaxNodeDegree(),this._maxNodeDegree},Fh.prototype.getShell=function(){return this._shell},Fh.prototype.mergeLabel=function(){if(1===arguments.length){var t=arguments[0];this.mergeLabel(t,0),this.mergeLabel(t,1)}else if(2===arguments.length){var e=arguments[0],n=arguments[1],r=e.getLocation(n,Sh.RIGHT);if(r===pl.NONE)return null;if(this._label.getLocation(n)===pl.NONE)return this._label.setLocation(n,r),null}},Fh.prototype.setShell=function(t){this._shell=t,null!==t&&t.addHole(this)},Fh.prototype.toPolygon=function(t){for(var e=new Array(this._holes.size()).fill(null),n=0;n=2,"found partial label"),this.computeIM(t)},qh.prototype.isInResult=function(){return this._isInResult},qh.prototype.isVisited=function(){return this._isVisited},qh.prototype.interfaces_=function(){return[]},qh.prototype.getClass=function(){return qh};var Bh=function(t){function e(){t.call(this),this._coord=null,this._edges=null;var e=arguments[0],n=arguments[1];this._coord=e,this._edges=n,this._label=new Dh(0,pl.NONE)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.isIncidentEdgeInResult=function(){for(var t=this.getEdges().getEdges().iterator();t.hasNext();){if(t.next().getEdge().isInResult())return!0}return!1},e.prototype.isIsolated=function(){return 1===this._label.getGeometryCount()},e.prototype.getCoordinate=function(){return this._coord},e.prototype.print=function(t){t.println("node "+this._coord+" lbl: "+this._label)},e.prototype.computeIM=function(t){},e.prototype.computeMergedLocation=function(t,e){var n=pl.NONE;if(n=this._label.getLocation(e),!t.isNull(e)){var r=t.getLocation(e);n!==pl.BOUNDARY&&(n=r)}return n},e.prototype.setLabel=function(){if(2!==arguments.length)return t.prototype.setLabel.apply(this,arguments);var e=arguments[0],n=arguments[1];null===this._label?this._label=new Dh(e,n):this._label.setLocation(e,n)},e.prototype.getEdges=function(){return this._edges},e.prototype.mergeLabel=function(){var t=this;if(arguments[0]instanceof e){var n=arguments[0];this.mergeLabel(n._label)}else if(arguments[0]instanceof Dh)for(var r=arguments[0],i=0;i<2;i++){var o=t.computeMergedLocation(r,i),s=t._label.getLocation(i);s===pl.NONE&&t._label.setLocation(i,o)}},e.prototype.add=function(t){this._edges.insert(t),t.setNode(this)},e.prototype.setLabelBoundary=function(t){if(null===this._label)return null;var e=pl.NONE;null!==this._label&&(e=this._label.getLocation(t));var n=null;switch(e){case pl.BOUNDARY:n=pl.INTERIOR;break;case pl.INTERIOR:default:n=pl.BOUNDARY}this._label.setLocation(t,n)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(qh),zh=function(){this.nodeMap=new Cc,this.nodeFact=null;var t=arguments[0];this.nodeFact=t};zh.prototype.find=function(t){return this.nodeMap.get(t)},zh.prototype.addNode=function(){if(arguments[0]instanceof ul){var t=arguments[0],e=this.nodeMap.get(t);return null===e&&(e=this.nodeFact.createNode(t),this.nodeMap.put(t,e)),e}if(arguments[0]instanceof Bh){var n=arguments[0],r=this.nodeMap.get(n.getCoordinate());return null===r?(this.nodeMap.put(n.getCoordinate(),n),n):(r.mergeLabel(n),r)}},zh.prototype.print=function(t){for(var e=this.iterator();e.hasNext();){e.next().print(t)}},zh.prototype.iterator=function(){return this.nodeMap.values().iterator()},zh.prototype.values=function(){return this.nodeMap.values()},zh.prototype.getBoundaryNodes=function(t){for(var e=new lc,n=this.iterator();n.hasNext();){var r=n.next();r.getLabel().getLocation(t)===pl.BOUNDARY&&e.add(r)}return e},zh.prototype.add=function(t){var e=t.getCoordinate();this.addNode(e).add(t)},zh.prototype.interfaces_=function(){return[]},zh.prototype.getClass=function(){return zh};var jh=function(){},Uh={NE:{configurable:!0},NW:{configurable:!0},SW:{configurable:!0},SE:{configurable:!0}};jh.prototype.interfaces_=function(){return[]},jh.prototype.getClass=function(){return jh},jh.isNorthern=function(t){return t===jh.NE||t===jh.NW},jh.isOpposite=function(t,e){return t!==e&&2===(t-e+4)%4},jh.commonHalfPlane=function(t,e){if(t===e)return t;if(2===(t-e+4)%4)return-1;var n=te?t:e)?3:n},jh.isInHalfPlane=function(t,e){return e===jh.SE?t===jh.SE||t===jh.SW:t===e||t===e+1},jh.quadrant=function(){if("number"==typeof arguments[0]&&"number"==typeof arguments[1]){var t=arguments[0],e=arguments[1];if(0===t&&0===e)throw new el("Cannot compute the quadrant for point ( "+t+", "+e+" )");return t>=0?e>=0?jh.NE:jh.SE:e>=0?jh.NW:jh.SW}if(arguments[0]instanceof ul&&arguments[1]instanceof ul){var n=arguments[0],r=arguments[1];if(r.x===n.x&&r.y===n.y)throw new el("Cannot compute the quadrant for two identical points "+n);return r.x>=n.x?r.y>=n.y?jh.NE:jh.SE:r.y>=n.y?jh.NW:jh.SW}},Uh.NE.get=function(){return 0},Uh.NW.get=function(){return 1},Uh.SW.get=function(){return 2},Uh.SE.get=function(){return 3},Object.defineProperties(jh,Uh);var Vh=function(){if(this._edge=null,this._label=null,this._node=null,this._p0=null,this._p1=null,this._dx=null,this._dy=null,this._quadrant=null,1===arguments.length){var t=arguments[0];this._edge=t}else if(3===arguments.length){var e=arguments[0],n=arguments[1],r=arguments[2],i=null;this._edge=e,this.init(n,r),this._label=i}else if(4===arguments.length){var o=arguments[0],s=arguments[1],a=arguments[2],u=arguments[3];this._edge=o,this.init(s,a),this._label=u}};Vh.prototype.compareDirection=function(t){return this._dx===t._dx&&this._dy===t._dy?0:this._quadrant>t._quadrant?1:this._quadrant2){o.linkDirectedEdgesForMinimalEdgeRings();var s=o.buildMinimalRings(),a=this.findShell(s);null!==a?(this.placePolygonHoles(a,s),e.add(a)):n.addAll(s)}else r.add(o)}return r},Wh.prototype.containsPoint=function(t){for(var e=this._shellList.iterator();e.hasNext();){if(e.next().containsPoint(t))return!0}return!1},Wh.prototype.buildMaximalEdgeRings=function(t){for(var e=new lc,n=t.iterator();n.hasNext();){var r=n.next();if(r.isInResult()&&r.getLabel().isArea()&&null===r.getEdgeRing()){var i=new Gh(r,this._geometryFactory);e.add(i),i.setInResult()}}return e},Wh.prototype.placePolygonHoles=function(t,e){for(var n=e.iterator();n.hasNext();){var r=n.next();r.isHole()&&r.setShell(t)}},Wh.prototype.getPolygons=function(){return this.computePolygons(this._shellList)},Wh.prototype.findEdgeRingContaining=function(t,e){for(var n=t.getLinearRing(),r=n.getEnvelopeInternal(),i=n.getCoordinateN(0),o=null,s=null,a=e.iterator();a.hasNext();){var u=a.next(),l=u.getLinearRing(),c=l.getEnvelopeInternal();null!==o&&(s=o.getLinearRing().getEnvelopeInternal());var h=!1;c.contains(r)&&Xl.isPointInRing(i,l.getCoordinates())&&(h=!0),h&&(null===o||s.contains(c))&&(o=u)}return o},Wh.prototype.findShell=function(t){for(var e=0,n=null,r=t.iterator();r.hasNext();){var i=r.next();i.isHole()||(n=i,e++)}return ql.isTrue(e<=1,"found two shells in MinimalEdgeRing list"),n},Wh.prototype.add=function(){if(1===arguments.length){var t=arguments[0];this.add(t.getEdgeEnds(),t.getNodes())}else if(2===arguments.length){var e=arguments[0],n=arguments[1];Hh.linkResultDirectedEdges(n);var r=this.buildMaximalEdgeRings(e),i=new lc,o=this.buildMinimalEdgeRings(r,this._shellList,i);this.sortShellsAndHoles(o,this._shellList,i),this.placeFreeHoles(this._shellList,i)}},Wh.prototype.interfaces_=function(){return[]},Wh.prototype.getClass=function(){return Wh};var Jh=function(){};Jh.prototype.getBounds=function(){},Jh.prototype.interfaces_=function(){return[]},Jh.prototype.getClass=function(){return Jh};var Zh=function(){this._bounds=null,this._item=null;var t=arguments[0],e=arguments[1];this._bounds=t,this._item=e};Zh.prototype.getItem=function(){return this._item},Zh.prototype.getBounds=function(){return this._bounds},Zh.prototype.interfaces_=function(){return[Jh,al]},Zh.prototype.getClass=function(){return Zh};var Kh=function(){this._size=null,this._items=null,this._size=0,this._items=new lc,this._items.add(null)};Kh.prototype.poll=function(){if(this.isEmpty())return null;var t=this._items.get(1);return this._items.set(1,this._items.get(this._size)),this._size-=1,this.reorder(1),t},Kh.prototype.size=function(){return this._size},Kh.prototype.reorder=function(t){for(var e=this,n=null,r=this._items.get(t);2*t<=this._size&&((n=2*t)!==e._size&&e._items.get(n+1).compareTo(e._items.get(n))<0&&n++,e._items.get(n).compareTo(r)<0);t=n)e._items.set(t,e._items.get(n));this._items.set(t,r)},Kh.prototype.clear=function(){this._size=0,this._items.clear()},Kh.prototype.isEmpty=function(){return 0===this._size},Kh.prototype.add=function(t){this._items.add(null),this._size+=1;var e=this._size;for(this._items.set(0,t);t.compareTo(this._items.get(Math.trunc(e/2)))<0;e/=2)this._items.set(e,this._items.get(Math.trunc(e/2)));this._items.set(e,t)},Kh.prototype.interfaces_=function(){return[]},Kh.prototype.getClass=function(){return Kh};var Qh=function(){};Qh.prototype.visitItem=function(t){},Qh.prototype.interfaces_=function(){return[]},Qh.prototype.getClass=function(){return Qh};var $h=function(){};$h.prototype.insert=function(t,e){},$h.prototype.remove=function(t,e){},$h.prototype.query=function(){},$h.prototype.interfaces_=function(){return[]},$h.prototype.getClass=function(){return $h};var tp=function(){if(this._childBoundables=new lc,this._bounds=null,this._level=null,0===arguments.length);else if(1===arguments.length){var t=arguments[0];this._level=t}},ep={serialVersionUID:{configurable:!0}};tp.prototype.getLevel=function(){return this._level},tp.prototype.size=function(){return this._childBoundables.size()},tp.prototype.getChildBoundables=function(){return this._childBoundables},tp.prototype.addChildBoundable=function(t){ql.isTrue(null===this._bounds),this._childBoundables.add(t)},tp.prototype.isEmpty=function(){return this._childBoundables.isEmpty()},tp.prototype.getBounds=function(){return null===this._bounds&&(this._bounds=this.computeBounds()),this._bounds},tp.prototype.interfaces_=function(){return[Jh,al]},tp.prototype.getClass=function(){return tp},ep.serialVersionUID.get=function(){return 0x5a1e55ec41369800},Object.defineProperties(tp,ep);var np=function(){};np.reverseOrder=function(){return{compare:function(t,e){return e.compareTo(t)}}},np.min=function(t){return np.sort(t),t.get(0)},np.sort=function(t,e){var n=t.toArray();e?Rc.sort(n,e):Rc.sort(n);for(var r=t.iterator(),i=0,o=n.length;irp.area(this._boundable2)?(this.expand(this._boundable1,this._boundable2,t,e),null):(this.expand(this._boundable2,this._boundable1,t,e),null);if(n)return this.expand(this._boundable1,this._boundable2,t,e),null;if(r)return this.expand(this._boundable2,this._boundable1,t,e),null;throw new el("neither boundable is composite")},rp.prototype.isLeaves=function(){return!(rp.isComposite(this._boundable1)||rp.isComposite(this._boundable2))},rp.prototype.compareTo=function(t){var e=t;return this._distancee._distance?1:0},rp.prototype.expand=function(t,e,n,r){for(var i=t.getChildBoundables().iterator();i.hasNext();){var o=i.next(),s=new rp(o,e,this._itemDistance);s.getDistance()1,"Node capacity must be greater than 1"),this._nodeCapacity=n}},op={IntersectsOp:{configurable:!0},serialVersionUID:{configurable:!0},DEFAULT_NODE_CAPACITY:{configurable:!0}};ip.prototype.getNodeCapacity=function(){return this._nodeCapacity},ip.prototype.lastNode=function(t){return t.get(t.size()-1)},ip.prototype.size=function(){var t=this;if(0===arguments.length)return this.isEmpty()?0:(this.build(),this.size(this._root));if(1===arguments.length){for(var e=arguments[0],n=0,r=e.getChildBoundables().iterator();r.hasNext();){var i=r.next();i instanceof tp?n+=t.size(i):i instanceof Zh&&(n+=1)}return n}},ip.prototype.removeItem=function(t,e){for(var n=null,r=t.getChildBoundables().iterator();r.hasNext();){var i=r.next();i instanceof Zh&&i.getItem()===e&&(n=i)}return null!==n&&(t.getChildBoundables().remove(n),!0)},ip.prototype.itemsTree=function(){var t=this;if(0===arguments.length){this.build();var e=this.itemsTree(this._root);return null===e?new lc:e}if(1===arguments.length){for(var n=arguments[0],r=new lc,i=n.getChildBoundables().iterator();i.hasNext();){var o=i.next();if(o instanceof tp){var s=t.itemsTree(o);null!==s&&r.add(s)}else o instanceof Zh?r.add(o.getItem()):ql.shouldNeverReachHere()}return r.size()<=0?null:r}},ip.prototype.insert=function(t,e){ql.isTrue(!this._built,"Cannot insert items into an STR packed R-tree after it has been built."),this._itemBoundables.add(new Zh(t,e))},ip.prototype.boundablesAtLevel=function(){var t=this;if(1===arguments.length){var e=arguments[0],n=new lc;return this.boundablesAtLevel(e,this._root,n),n}if(3===arguments.length){var r=arguments[0],i=arguments[1],o=arguments[2];if(ql.isTrue(r>-2),i.getLevel()===r)return o.add(i),null;for(var s=i.getChildBoundables().iterator();s.hasNext();){var a=s.next();a instanceof tp?t.boundablesAtLevel(r,a,o):(ql.isTrue(a instanceof Zh),-1===r&&o.add(a))}return null}},ip.prototype.query=function(){var t=this;if(1===arguments.length){var e=arguments[0];this.build();var n=new lc;return this.isEmpty()||this.getIntersectsOp().intersects(this._root.getBounds(),e)&&this.query(e,this._root,n),n}if(2===arguments.length){var r=arguments[0],i=arguments[1];if(this.build(),this.isEmpty())return null;this.getIntersectsOp().intersects(this._root.getBounds(),r)&&this.query(r,this._root,i)}else if(3===arguments.length)if(gl(arguments[2],Qh)&&arguments[0]instanceof Object&&arguments[1]instanceof tp)for(var o=arguments[0],s=arguments[1],a=arguments[2],u=s.getChildBoundables(),l=0;ln&&(n=o)}}return n+1}},ip.prototype.createParentBoundables=function(t,e){var n=this;ql.isTrue(!t.isEmpty());var r=new lc;r.add(this.createNode(e));var i=new lc(t);np.sort(i,this.getComparator());for(var o=i.iterator();o.hasNext();){var s=o.next();n.lastNode(r).getChildBoundables().size()===n.getNodeCapacity()&&r.add(n.createNode(e)),n.lastNode(r).addChildBoundable(s)}return r},ip.prototype.isEmpty=function(){return this._built?this._root.isEmpty():this._itemBoundables.isEmpty()},ip.prototype.interfaces_=function(){return[al]},ip.prototype.getClass=function(){return ip},ip.compareDoubles=function(t,e){return t>e?1:t0);for(var n=new lc,r=0;r0;){var p=h.poll(),f=p.getDistance();if(f>=l)break;p.isLeaves()?(l=f,c=p):p.expandToQueue(h,l)}return[c.getBoundable(0).getItem(),c.getBoundable(1).getItem()]}}else if(3===arguments.length){var g=arguments[0],d=arguments[1],y=arguments[2],v=new Zh(g,d),_=new rp(this.getRoot(),v,y);return this.nearestNeighbour(_)[0]}},e.prototype.interfaces_=function(){return[$h,al]},e.prototype.getClass=function(){return e},e.centreX=function(t){return e.avg(t.getMinX(),t.getMaxX())},e.avg=function(t,e){return(t+e)/2},e.centreY=function(t){return e.avg(t.getMinY(),t.getMaxY())},n.STRtreeNode.get=function(){return lp},n.serialVersionUID.get=function(){return 0x39920f7d5f261e0},n.xComparator.get=function(){return{interfaces_:function(){return[sl]},compare:function(n,r){return t.compareDoubles(e.centreX(n.getBounds()),e.centreX(r.getBounds()))}}},n.yComparator.get=function(){return{interfaces_:function(){return[sl]},compare:function(n,r){return t.compareDoubles(e.centreY(n.getBounds()),e.centreY(r.getBounds()))}}},n.intersectsOp.get=function(){return{interfaces_:function(){return[t.IntersectsOp]},intersects:function(t,e){return t.intersects(e)}}},n.DEFAULT_NODE_CAPACITY.get=function(){return 10},Object.defineProperties(e,n),e}(ip),lp=function(t){function e(){var e=arguments[0];t.call(this,e)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.computeBounds=function(){for(var t=null,e=this.getChildBoundables().iterator();e.hasNext();){var n=e.next();null===t?t=new Ll(n.getBounds()):t.expandToInclude(n.getBounds())}return t},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(tp),cp=function(){};cp.prototype.interfaces_=function(){return[]},cp.prototype.getClass=function(){return cp},cp.relativeSign=function(t,e){return te?1:0},cp.compare=function(t,e,n){if(e.equals2D(n))return 0;var r=cp.relativeSign(e.x,n.x),i=cp.relativeSign(e.y,n.y);switch(t){case 0:return cp.compareValue(r,i);case 1:return cp.compareValue(i,r);case 2:return cp.compareValue(i,-r);case 3:return cp.compareValue(-r,i);case 4:return cp.compareValue(-r,-i);case 5:return cp.compareValue(-i,-r);case 6:return cp.compareValue(-i,r);case 7:return cp.compareValue(r,-i)}return ql.shouldNeverReachHere("invalid octant value"),0},cp.compareValue=function(t,e){return t<0?-1:t>0?1:e<0?-1:e>0?1:0};var hp=function(){this._segString=null,this.coord=null,this.segmentIndex=null,this._segmentOctant=null,this._isInterior=null;var t=arguments[0],e=arguments[1],n=arguments[2],r=arguments[3];this._segString=t,this.coord=new ul(e),this.segmentIndex=n,this._segmentOctant=r,this._isInterior=!e.equals2D(t.getCoordinate(n))};hp.prototype.getCoordinate=function(){return this.coord},hp.prototype.print=function(t){t.print(this.coord),t.print(" seg # = "+this.segmentIndex)},hp.prototype.compareTo=function(t){var e=t;return this.segmentIndexe.segmentIndex?1:this.coord.equals2D(e.coord)?0:cp.compare(this._segmentOctant,this.coord,e.coord)},hp.prototype.isEndPoint=function(t){return 0===this.segmentIndex&&!this._isInterior||this.segmentIndex===t},hp.prototype.isInterior=function(){return this._isInterior},hp.prototype.interfaces_=function(){return[il]},hp.prototype.getClass=function(){return hp};var pp=function(){this._nodeMap=new Cc,this._edge=null;var t=arguments[0];this._edge=t};pp.prototype.getSplitCoordinates=function(){var t=new hc;this.addEndpoints();for(var e=this.iterator(),n=e.next();e.hasNext();){var r=e.next();this.addEdgeCoordinates(n,r,t),n=r}return t.toCoordinateArray()},pp.prototype.addCollapsedNodes=function(){var t=new lc;this.findCollapsesFromInsertedNodes(t),this.findCollapsesFromExistingVertices(t);for(var e=t.iterator();e.hasNext();){var n=e.next().intValue();this.add(this._edge.getCoordinate(n),n)}},pp.prototype.print=function(t){t.println("Intersections:");for(var e=this.iterator();e.hasNext();){e.next().print(t)}},pp.prototype.findCollapsesFromExistingVertices=function(t){for(var e=0;e=0?e>=0?n>=r?0:1:n>=r?7:6:e>=0?n>=r?3:2:n>=r?4:5}if(arguments[0]instanceof ul&&arguments[1]instanceof ul){var i=arguments[0],o=arguments[1],s=o.x-i.x,a=o.y-i.y;if(0===s&&0===a)throw new el("Cannot compute the octant for two identical points "+i);return fp.octant(s,a)}};var gp=function(){};gp.prototype.getCoordinates=function(){},gp.prototype.size=function(){},gp.prototype.getCoordinate=function(t){},gp.prototype.isClosed=function(){},gp.prototype.setData=function(t){},gp.prototype.getData=function(){},gp.prototype.interfaces_=function(){return[]},gp.prototype.getClass=function(){return gp};var dp=function(){};dp.prototype.addIntersection=function(t,e){},dp.prototype.interfaces_=function(){return[gp]},dp.prototype.getClass=function(){return dp};var yp=function(){this._nodeList=new pp(this),this._pts=null,this._data=null;var t=arguments[0],e=arguments[1];this._pts=t,this._data=e};yp.prototype.getCoordinates=function(){return this._pts},yp.prototype.size=function(){return this._pts.length},yp.prototype.getCoordinate=function(t){return this._pts[t]},yp.prototype.isClosed=function(){return this._pts[0].equals(this._pts[this._pts.length-1])},yp.prototype.getSegmentOctant=function(t){return t===this._pts.length-1?-1:this.safeOctant(this.getCoordinate(t),this.getCoordinate(t+1))},yp.prototype.setData=function(t){this._data=t},yp.prototype.safeOctant=function(t,e){return t.equals2D(e)?0:fp.octant(t,e)},yp.prototype.getData=function(){return this._data},yp.prototype.addIntersection=function(){if(2===arguments.length){var t=arguments[0],e=arguments[1];this.addIntersectionNode(t,e)}else if(4===arguments.length){var n=arguments[0],r=arguments[1],i=arguments[3],o=new ul(n.getIntersection(i));this.addIntersection(o,r)}},yp.prototype.toString=function(){return Fl.toLineString(new lh(this._pts))},yp.prototype.getNodeList=function(){return this._nodeList},yp.prototype.addIntersectionNode=function(t,e){var n=e,r=n+1;if(r=0&&n>=0||e<=0&&n<=0?Math.max(e,n):0}if(arguments[0]instanceof ul){var r=arguments[0];return Xl.orientationIndex(this.p0,this.p1,r)}},vp.prototype.toGeometry=function(t){return t.createLineString([this.p0,this.p1])},vp.prototype.isVertical=function(){return this.p0.x===this.p1.x},vp.prototype.equals=function(t){if(!(t instanceof vp))return!1;var e=t;return this.p0.equals(e.p0)&&this.p1.equals(e.p1)},vp.prototype.intersection=function(t){var e=new jl;return e.computeIntersection(this.p0,this.p1,t.p0,t.p1),e.hasIntersection()?e.getIntersection(0):null},vp.prototype.project=function(){if(arguments[0]instanceof ul){var t=arguments[0];if(t.equals(this.p0)||t.equals(this.p1))return new ul(t);var e=this.projectionFactor(t),n=new ul;return n.x=this.p0.x+e*(this.p1.x-this.p0.x),n.y=this.p0.y+e*(this.p1.y-this.p0.y),n}if(arguments[0]instanceof vp){var r=arguments[0],i=this.projectionFactor(r.p0),o=this.projectionFactor(r.p1);if(i>=1&&o>=1)return null;if(i<=0&&o<=0)return null;var s=this.project(r.p0);i<0&&(s=this.p0),i>1&&(s=this.p1);var a=this.project(r.p1);return o<0&&(a=this.p0),o>1&&(a=this.p1),new vp(s,a)}},vp.prototype.normalize=function(){this.p1.compareTo(this.p0)<0&&this.reverse()},vp.prototype.angle=function(){return Math.atan2(this.p1.y-this.p0.y,this.p1.x-this.p0.x)},vp.prototype.getCoordinate=function(t){return 0===t?this.p0:this.p1},vp.prototype.distancePerpendicular=function(t){return Xl.distancePointLinePerpendicular(t,this.p0,this.p1)},vp.prototype.minY=function(){return Math.min(this.p0.y,this.p1.y)},vp.prototype.midPoint=function(){return vp.midPoint(this.p0,this.p1)},vp.prototype.projectionFactor=function(t){if(t.equals(this.p0))return 0;if(t.equals(this.p1))return 1;var e=this.p1.x-this.p0.x,n=this.p1.y-this.p0.y,r=e*e+n*n;return r<=0?nl.NaN:((t.x-this.p0.x)*e+(t.y-this.p0.y)*n)/r},vp.prototype.closestPoints=function(t){var e=this.intersection(t);if(null!==e)return[e,e];var n=new Array(2).fill(null),r=nl.MAX_VALUE,i=null,o=this.closestPoint(t.p0);r=o.distance(t.p0),n[0]=o,n[1]=t.p0;var s=this.closestPoint(t.p1);(i=s.distance(t.p1))0&&e<1?this.project(t):this.p0.distance(t)1||nl.isNaN(e))&&(e=1),e},vp.prototype.toString=function(){return"LINESTRING( "+this.p0.x+" "+this.p0.y+", "+this.p1.x+" "+this.p1.y+")"},vp.prototype.isHorizontal=function(){return this.p0.y===this.p1.y},vp.prototype.distance=function(){if(arguments[0]instanceof vp){var t=arguments[0];return Xl.distanceLineLine(this.p0,this.p1,t.p0,t.p1)}if(arguments[0]instanceof ul){var e=arguments[0];return Xl.distancePointLine(e,this.p0,this.p1)}},vp.prototype.pointAlong=function(t){var e=new ul;return e.x=this.p0.x+t*(this.p1.x-this.p0.x),e.y=this.p0.y+t*(this.p1.y-this.p0.y),e},vp.prototype.hashCode=function(){var t=nl.doubleToLongBits(this.p0.x);t^=31*nl.doubleToLongBits(this.p0.y);var e=Math.trunc(t)^Math.trunc(t>>32),n=nl.doubleToLongBits(this.p1.x);return n^=31*nl.doubleToLongBits(this.p1.y),e^(Math.trunc(n)^Math.trunc(n>>32))},vp.prototype.interfaces_=function(){return[il,al]},vp.prototype.getClass=function(){return vp},vp.midPoint=function(t,e){return new ul((t.x+e.x)/2,(t.y+e.y)/2)},_p.serialVersionUID.get=function(){return 0x2d2172135f411c00},Object.defineProperties(vp,_p);var mp=function(){this.tempEnv1=new Ll,this.tempEnv2=new Ll,this._overlapSeg1=new vp,this._overlapSeg2=new vp};mp.prototype.overlap=function(){if(2===arguments.length);else if(4===arguments.length){var t=arguments[0],e=arguments[1],n=arguments[2],r=arguments[3];t.getLineSegment(e,this._overlapSeg1),n.getLineSegment(r,this._overlapSeg2),this.overlap(this._overlapSeg1,this._overlapSeg2)}},mp.prototype.interfaces_=function(){return[]},mp.prototype.getClass=function(){return mp};var xp=function(){this._pts=null,this._start=null,this._end=null,this._env=null,this._context=null,this._id=null;var t=arguments[0],e=arguments[1],n=arguments[2],r=arguments[3];this._pts=t,this._start=e,this._end=n,this._context=r};xp.prototype.getLineSegment=function(t,e){e.p0=this._pts[t],e.p1=this._pts[t+1]},xp.prototype.computeSelect=function(t,e,n,r){var i=this._pts[e],o=this._pts[n];if(r.tempEnv1.init(i,o),n-e==1)return r.select(this,e),null;if(!t.intersects(r.tempEnv1))return null;var s=Math.trunc((e+n)/2);e=t.length-1)return t.length-1;for(var r=jh.quadrant(t[n],t[n+1]),i=e+1;ir.getId()&&(r.computeOverlaps(o,e),t._nOverlaps++),t._segInt.isDone())return null}},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},n.SegmentOverlapAction.get=function(){return Np},Object.defineProperties(e,n),e}(wp),Np=function(t){function e(){t.call(this),this._si=null;var e=arguments[0];this._si=e}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.overlap=function(){if(4!==arguments.length)return t.prototype.overlap.apply(this,arguments);var e=arguments[0],n=arguments[1],r=arguments[2],i=arguments[3],o=e.getContext(),s=r.getContext();this._si.processIntersections(o,n,s,i)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(mp),Sp=function t(){if(this._quadrantSegments=t.DEFAULT_QUADRANT_SEGMENTS,this._endCapStyle=t.CAP_ROUND,this._joinStyle=t.JOIN_ROUND,this._mitreLimit=t.DEFAULT_MITRE_LIMIT,this._isSingleSided=!1,this._simplifyFactor=t.DEFAULT_SIMPLIFY_FACTOR,0===arguments.length);else if(1===arguments.length){var e=arguments[0];this.setQuadrantSegments(e)}else if(2===arguments.length){var n=arguments[0],r=arguments[1];this.setQuadrantSegments(n),this.setEndCapStyle(r)}else if(4===arguments.length){var i=arguments[0],o=arguments[1],s=arguments[2],a=arguments[3];this.setQuadrantSegments(i),this.setEndCapStyle(o),this.setJoinStyle(s),this.setMitreLimit(a)}},Cp={CAP_ROUND:{configurable:!0},CAP_FLAT:{configurable:!0},CAP_SQUARE:{configurable:!0},JOIN_ROUND:{configurable:!0},JOIN_MITRE:{configurable:!0},JOIN_BEVEL:{configurable:!0},DEFAULT_QUADRANT_SEGMENTS:{configurable:!0},DEFAULT_MITRE_LIMIT:{configurable:!0},DEFAULT_SIMPLIFY_FACTOR:{configurable:!0}};Sp.prototype.getEndCapStyle=function(){return this._endCapStyle},Sp.prototype.isSingleSided=function(){return this._isSingleSided},Sp.prototype.setQuadrantSegments=function(t){this._quadrantSegments=t,0===this._quadrantSegments&&(this._joinStyle=Sp.JOIN_BEVEL),this._quadrantSegments<0&&(this._joinStyle=Sp.JOIN_MITRE,this._mitreLimit=Math.abs(this._quadrantSegments)),t<=0&&(this._quadrantSegments=1),this._joinStyle!==Sp.JOIN_ROUND&&(this._quadrantSegments=Sp.DEFAULT_QUADRANT_SEGMENTS)},Sp.prototype.getJoinStyle=function(){return this._joinStyle},Sp.prototype.setJoinStyle=function(t){this._joinStyle=t},Sp.prototype.setSimplifyFactor=function(t){this._simplifyFactor=t<0?0:t},Sp.prototype.getSimplifyFactor=function(){return this._simplifyFactor},Sp.prototype.getQuadrantSegments=function(){return this._quadrantSegments},Sp.prototype.setEndCapStyle=function(t){this._endCapStyle=t},Sp.prototype.getMitreLimit=function(){return this._mitreLimit},Sp.prototype.setMitreLimit=function(t){this._mitreLimit=t},Sp.prototype.setSingleSided=function(t){this._isSingleSided=t},Sp.prototype.interfaces_=function(){return[]},Sp.prototype.getClass=function(){return Sp},Sp.bufferDistanceError=function(t){var e=Math.PI/2/t;return 1-Math.cos(e/2)},Cp.CAP_ROUND.get=function(){return 1},Cp.CAP_FLAT.get=function(){return 2},Cp.CAP_SQUARE.get=function(){return 3},Cp.JOIN_ROUND.get=function(){return 1},Cp.JOIN_MITRE.get=function(){return 2},Cp.JOIN_BEVEL.get=function(){return 3},Cp.DEFAULT_QUADRANT_SEGMENTS.get=function(){return 8},Cp.DEFAULT_MITRE_LIMIT.get=function(){return 5},Cp.DEFAULT_SIMPLIFY_FACTOR.get=function(){return.01},Object.defineProperties(Sp,Cp);var Pp=function(t){this._distanceTol=null,this._isDeleted=null,this._angleOrientation=Xl.COUNTERCLOCKWISE,this._inputLine=t||null},Mp={INIT:{configurable:!0},DELETE:{configurable:!0},KEEP:{configurable:!0},NUM_PTS_TO_CHECK:{configurable:!0}};Pp.prototype.isDeletable=function(t,e,n,r){var i=this._inputLine[t],o=this._inputLine[e],s=this._inputLine[n];return!!this.isConcave(i,o,s)&&(!!this.isShallow(i,o,s,r)&&this.isShallowSampled(i,o,t,n,r))},Pp.prototype.deleteShallowConcavities=function(){for(var t=this,e=1,n=this.findNextNonDeletedIndex(e),r=this.findNextNonDeletedIndex(n),i=!1;r=0;r--)this.addPt(t[r])},Lp.prototype.isRedundant=function(t){if(this._ptList.size()<1)return!1;var e=this._ptList.get(this._ptList.size()-1);return t.distance(e)Math.PI;)t-=Rp.PI_TIMES_2;for(;t<=-Math.PI;)t+=Rp.PI_TIMES_2;return t},Rp.angle=function(){if(1===arguments.length){var t=arguments[0];return Math.atan2(t.y,t.x)}if(2===arguments.length){var e=arguments[0],n=arguments[1],r=n.x-e.x,i=n.y-e.y;return Math.atan2(i,r)}},Rp.isAcute=function(t,e,n){var r=t.x-e.x,i=t.y-e.y;return r*(n.x-e.x)+i*(n.y-e.y)>0},Rp.isObtuse=function(t,e,n){var r=t.x-e.x,i=t.y-e.y;return r*(n.x-e.x)+i*(n.y-e.y)<0},Rp.interiorAngle=function(t,e,n){var r=Rp.angle(e,t),i=Rp.angle(e,n);return Math.abs(i-r)},Rp.normalizePositive=function(t){if(t<0){for(;t<0;)t+=Rp.PI_TIMES_2;t>=Rp.PI_TIMES_2&&(t=0)}else{for(;t>=Rp.PI_TIMES_2;)t-=Rp.PI_TIMES_2;t<0&&(t=0)}return t},Rp.angleBetween=function(t,e,n){var r=Rp.angle(e,t),i=Rp.angle(e,n);return Rp.diff(r,i)},Rp.diff=function(t,e){var n=null;return(n=tMath.PI&&(n=2*Math.PI-n),n},Rp.toRadians=function(t){return t*Math.PI/180},Rp.getTurn=function(t,e){var n=Math.sin(e-t);return n>0?Rp.COUNTERCLOCKWISE:n<0?Rp.CLOCKWISE:Rp.NONE},Rp.angleBetweenOriented=function(t,e,n){var r=Rp.angle(e,t),i=Rp.angle(e,n)-r;return i<=-Math.PI?i+Rp.PI_TIMES_2:i>Math.PI?i-Rp.PI_TIMES_2:i},Tp.PI_TIMES_2.get=function(){return 2*Math.PI},Tp.PI_OVER_2.get=function(){return Math.PI/2},Tp.PI_OVER_4.get=function(){return Math.PI/4},Tp.COUNTERCLOCKWISE.get=function(){return Xl.COUNTERCLOCKWISE},Tp.CLOCKWISE.get=function(){return Xl.CLOCKWISE},Tp.NONE.get=function(){return Xl.COLLINEAR},Object.defineProperties(Rp,Tp);var Ap=function t(){this._maxCurveSegmentError=0,this._filletAngleQuantum=null,this._closingSegLengthFactor=1,this._segList=null,this._distance=0,this._precisionModel=null,this._bufParams=null,this._li=null,this._s0=null,this._s1=null,this._s2=null,this._seg0=new vp,this._seg1=new vp,this._offset0=new vp,this._offset1=new vp,this._side=0,this._hasNarrowConcaveAngle=!1;var e=arguments[0],n=arguments[1],r=arguments[2];this._precisionModel=e,this._bufParams=n,this._li=new jl,this._filletAngleQuantum=Math.PI/2/n.getQuadrantSegments(),n.getQuadrantSegments()>=8&&n.getJoinStyle()===Sp.JOIN_ROUND&&(this._closingSegLengthFactor=t.MAX_CLOSING_SEG_LEN_FACTOR),this.init(r)},Dp={OFFSET_SEGMENT_SEPARATION_FACTOR:{configurable:!0},INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR:{configurable:!0},CURVE_VERTEX_SNAP_DISTANCE_FACTOR:{configurable:!0},MAX_CLOSING_SEG_LEN_FACTOR:{configurable:!0}};Ap.prototype.addNextSegment=function(t,e){if(this._s0=this._s1,this._s1=this._s2,this._s2=t,this._seg0.setCoordinates(this._s0,this._s1),this.computeOffsetSegment(this._seg0,this._side,this._distance,this._offset0),this._seg1.setCoordinates(this._s1,this._s2),this.computeOffsetSegment(this._seg1,this._side,this._distance,this._offset1),this._s1.equals(this._s2))return null;var n=Xl.computeOrientation(this._s0,this._s1,this._s2),r=n===Xl.CLOCKWISE&&this._side===Sh.LEFT||n===Xl.COUNTERCLOCKWISE&&this._side===Sh.RIGHT;0===n?this.addCollinear(e):r?this.addOutsideTurn(n,e):this.addInsideTurn(n,e)},Ap.prototype.addLineEndCap=function(t,e){var n=new vp(t,e),r=new vp;this.computeOffsetSegment(n,Sh.LEFT,this._distance,r);var i=new vp;this.computeOffsetSegment(n,Sh.RIGHT,this._distance,i);var o=e.x-t.x,s=e.y-t.y,a=Math.atan2(s,o);switch(this._bufParams.getEndCapStyle()){case Sp.CAP_ROUND:this._segList.addPt(r.p1),this.addFilletArc(e,a+Math.PI/2,a-Math.PI/2,Xl.CLOCKWISE,this._distance),this._segList.addPt(i.p1);break;case Sp.CAP_FLAT:this._segList.addPt(r.p1),this._segList.addPt(i.p1);break;case Sp.CAP_SQUARE:var u=new ul;u.x=Math.abs(this._distance)*Math.cos(a),u.y=Math.abs(this._distance)*Math.sin(a);var l=new ul(r.p1.x+u.x,r.p1.y+u.y),c=new ul(i.p1.x+u.x,i.p1.y+u.y);this._segList.addPt(l),this._segList.addPt(c)}},Ap.prototype.getCoordinates=function(){return this._segList.getCoordinates()},Ap.prototype.addMitreJoin=function(t,e,n,r){var i=!0,o=null;try{o=Ml.intersection(e.p0,e.p1,n.p0,n.p1),(r<=0?1:o.distance(t)/Math.abs(r))>this._bufParams.getMitreLimit()&&(i=!1)}catch(t){if(!(t instanceof Cl))throw t;o=new ul(0,0),i=!1}i?this._segList.addPt(o):this.addLimitedMitreJoin(e,n,r,this._bufParams.getMitreLimit())},Ap.prototype.addFilletCorner=function(t,e,n,r,i){var o=e.x-t.x,s=e.y-t.y,a=Math.atan2(s,o),u=n.x-t.x,l=n.y-t.y,c=Math.atan2(l,u);r===Xl.CLOCKWISE?a<=c&&(a+=2*Math.PI):a>=c&&(a-=2*Math.PI),this._segList.addPt(e),this.addFilletArc(t,a,c,r,i),this._segList.addPt(n)},Ap.prototype.addOutsideTurn=function(t,e){if(this._offset0.p1.distance(this._offset1.p0)0){var n=new ul((this._closingSegLengthFactor*this._offset0.p1.x+this._s1.x)/(this._closingSegLengthFactor+1),(this._closingSegLengthFactor*this._offset0.p1.y+this._s1.y)/(this._closingSegLengthFactor+1));this._segList.addPt(n);var r=new ul((this._closingSegLengthFactor*this._offset1.p0.x+this._s1.x)/(this._closingSegLengthFactor+1),(this._closingSegLengthFactor*this._offset1.p0.y+this._s1.y)/(this._closingSegLengthFactor+1));this._segList.addPt(r)}else this._segList.addPt(this._s1);this._segList.addPt(this._offset1.p0)}},Ap.prototype.createCircle=function(t){var e=new ul(t.x+this._distance,t.y);this._segList.addPt(e),this.addFilletArc(t,0,2*Math.PI,-1,this._distance),this._segList.closeRing()},Ap.prototype.addBevelJoin=function(t,e){this._segList.addPt(t.p1),this._segList.addPt(e.p0)},Ap.prototype.init=function(t){this._distance=t,this._maxCurveSegmentError=t*(1-Math.cos(this._filletAngleQuantum/2)),this._segList=new Lp,this._segList.setPrecisionModel(this._precisionModel),this._segList.setMinimumVertexDistance(t*Ap.CURVE_VERTEX_SNAP_DISTANCE_FACTOR)},Ap.prototype.addCollinear=function(t){this._li.computeIntersection(this._s0,this._s1,this._s1,this._s2),this._li.getIntersectionNum()>=2&&(this._bufParams.getJoinStyle()===Sp.JOIN_BEVEL||this._bufParams.getJoinStyle()===Sp.JOIN_MITRE?(t&&this._segList.addPt(this._offset0.p1),this._segList.addPt(this._offset1.p0)):this.addFilletCorner(this._s1,this._offset0.p1,this._offset1.p0,Xl.CLOCKWISE,this._distance))},Ap.prototype.closeRing=function(){this._segList.closeRing()},Ap.prototype.hasNarrowConcaveAngle=function(){return this._hasNarrowConcaveAngle},Ap.prototype.interfaces_=function(){return[]},Ap.prototype.getClass=function(){return Ap},Dp.OFFSET_SEGMENT_SEPARATION_FACTOR.get=function(){return.001},Dp.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get=function(){return.001},Dp.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get=function(){return 1e-6},Dp.MAX_CLOSING_SEG_LEN_FACTOR.get=function(){return 80},Object.defineProperties(Ap,Dp);var Fp=function(){this._distance=0,this._precisionModel=null,this._bufParams=null;var t=arguments[0],e=arguments[1];this._precisionModel=t,this._bufParams=e};Fp.prototype.getOffsetCurve=function(t,e){if(this._distance=e,0===e)return null;var n=e<0,r=Math.abs(e),i=this.getSegGen(r);t.length<=1?this.computePointCurve(t[0],i):this.computeOffsetCurve(t,n,i);var o=i.getCoordinates();return n&&pc.reverse(o),o},Fp.prototype.computeSingleSidedBufferCurve=function(t,e,n){var r=this.simplifyTolerance(this._distance);if(e){n.addSegments(t,!0);var i=Pp.simplify(t,-r),o=i.length-1;n.initSideSegments(i[o],i[o-1],Sh.LEFT),n.addFirstSegment();for(var s=o-2;s>=0;s--)n.addNextSegment(i[s],!0)}else{n.addSegments(t,!1);var a=Pp.simplify(t,r),u=a.length-1;n.initSideSegments(a[0],a[1],Sh.LEFT),n.addFirstSegment();for(var l=2;l<=u;l++)n.addNextSegment(a[l],!0)}n.addLastSegment(),n.closeRing()},Fp.prototype.computeRingBufferCurve=function(t,e,n){var r=this.simplifyTolerance(this._distance);e===Sh.RIGHT&&(r=-r);var i=Pp.simplify(t,r),o=i.length-1;n.initSideSegments(i[o-1],i[0],e);for(var s=1;s<=o;s++){var a=1!==s;n.addNextSegment(i[s],a)}n.closeRing()},Fp.prototype.computeLineBufferCurve=function(t,e){var n=this.simplifyTolerance(this._distance),r=Pp.simplify(t,n),i=r.length-1;e.initSideSegments(r[0],r[1],Sh.LEFT);for(var o=2;o<=i;o++)e.addNextSegment(r[o],!0);e.addLastSegment(),e.addLineEndCap(r[i-1],r[i]);var s=Pp.simplify(t,-n),a=s.length-1;e.initSideSegments(s[a],s[a-1],Sh.LEFT);for(var u=a-2;u>=0;u--)e.addNextSegment(s[u],!0);e.addLastSegment(),e.addLineEndCap(s[1],s[0]),e.closeRing()},Fp.prototype.computePointCurve=function(t,e){switch(this._bufParams.getEndCapStyle()){case Sp.CAP_ROUND:e.createCircle(t);break;case Sp.CAP_SQUARE:e.createSquare(t)}},Fp.prototype.getLineCurve=function(t,e){if(this._distance=e,e<0&&!this._bufParams.isSingleSided())return null;if(0===e)return null;var n=Math.abs(e),r=this.getSegGen(n);if(t.length<=1)this.computePointCurve(t[0],r);else if(this._bufParams.isSingleSided()){var i=e<0;this.computeSingleSidedBufferCurve(t,i,r)}else this.computeLineBufferCurve(t,r);return r.getCoordinates()},Fp.prototype.getBufferParameters=function(){return this._bufParams},Fp.prototype.simplifyTolerance=function(t){return t*this._bufParams.getSimplifyFactor()},Fp.prototype.getRingCurve=function(t,e,n){if(this._distance=n,t.length<=2)return this.getLineCurve(t,n);if(0===n)return Fp.copyCoordinates(t);var r=this.getSegGen(n);return this.computeRingBufferCurve(t,e,r),r.getCoordinates()},Fp.prototype.computeOffsetCurve=function(t,e,n){var r=this.simplifyTolerance(this._distance);if(e){var i=Pp.simplify(t,-r),o=i.length-1;n.initSideSegments(i[o],i[o-1],Sh.LEFT),n.addFirstSegment();for(var s=o-2;s>=0;s--)n.addNextSegment(i[s],!0)}else{var a=Pp.simplify(t,r),u=a.length-1;n.initSideSegments(a[0],a[1],Sh.LEFT),n.addFirstSegment();for(var l=2;l<=u;l++)n.addNextSegment(a[l],!0)}n.addLastSegment()},Fp.prototype.getSegGen=function(t){return new Ap(this._precisionModel,this._bufParams,t)},Fp.prototype.interfaces_=function(){return[]},Fp.prototype.getClass=function(){return Fp},Fp.copyCoordinates=function(t){for(var e=new Array(t.length).fill(null),n=0;no.getMaxY()||t.findStabbedSegments(e,i.getDirectedEdges(),n)}return n}if(3===arguments.length)if(gl(arguments[2],ac)&&arguments[0]instanceof ul&&arguments[1]instanceof Xh)for(var s=arguments[0],a=arguments[1],u=arguments[2],l=a.getEdge().getCoordinates(),c=0;ct._seg.p1.y&&t._seg.reverse();var h=Math.max(t._seg.p0.x,t._seg.p1.x);if(!(ht._seg.p1.y||Xl.computeOrientation(t._seg.p0,t._seg.p1,s)===Xl.RIGHT)){var p=a.getDepth(Sh.LEFT);t._seg.p0.equals(l[c])||(p=a.getDepth(Sh.RIGHT));var f=new qp(t._seg,p);u.add(f)}}else if(gl(arguments[2],ac)&&arguments[0]instanceof ul&&gl(arguments[1],ac))for(var g=arguments[0],d=arguments[1],y=arguments[2],v=d.iterator();v.hasNext();){var _=v.next();_.isForward()&&t.findStabbedSegments(g,_,y)}},kp.prototype.getDepth=function(t){var e=this.findStabbedSegments(t);return 0===e.size()?0:np.min(e)._leftDepth},kp.prototype.interfaces_=function(){return[]},kp.prototype.getClass=function(){return kp},Gp.DepthSegment.get=function(){return qp},Object.defineProperties(kp,Gp);var qp=function(){this._upwardSeg=null,this._leftDepth=null;var t=arguments[0],e=arguments[1];this._upwardSeg=new vp(t),this._leftDepth=e};qp.prototype.compareTo=function(t){var e=t;if(this._upwardSeg.minX()>=e._upwardSeg.maxX())return 1;if(this._upwardSeg.maxX()<=e._upwardSeg.minX())return-1;var n=this._upwardSeg.orientationIndex(e._upwardSeg);return 0!==n||0!==(n=-1*e._upwardSeg.orientationIndex(this._upwardSeg))?n:this._upwardSeg.compareTo(e._upwardSeg)},qp.prototype.compareX=function(t,e){var n=t.p0.compareTo(e.p0);return 0!==n?n:t.p1.compareTo(e.p1)},qp.prototype.toString=function(){return this._upwardSeg.toString()},qp.prototype.interfaces_=function(){return[il]},qp.prototype.getClass=function(){return qp};var Bp=function(t,e,n){this.p0=t||null,this.p1=e||null,this.p2=n||null};Bp.prototype.area=function(){return Bp.area(this.p0,this.p1,this.p2)},Bp.prototype.signedArea=function(){return Bp.signedArea(this.p0,this.p1,this.p2)},Bp.prototype.interpolateZ=function(t){if(null===t)throw new el("Supplied point is null.");return Bp.interpolateZ(t,this.p0,this.p1,this.p2)},Bp.prototype.longestSideLength=function(){return Bp.longestSideLength(this.p0,this.p1,this.p2)},Bp.prototype.isAcute=function(){return Bp.isAcute(this.p0,this.p1,this.p2)},Bp.prototype.circumcentre=function(){return Bp.circumcentre(this.p0,this.p1,this.p2)},Bp.prototype.area3D=function(){return Bp.area3D(this.p0,this.p1,this.p2)},Bp.prototype.centroid=function(){return Bp.centroid(this.p0,this.p1,this.p2)},Bp.prototype.inCentre=function(){return Bp.inCentre(this.p0,this.p1,this.p2)},Bp.prototype.interfaces_=function(){return[]},Bp.prototype.getClass=function(){return Bp},Bp.area=function(t,e,n){return Math.abs(((n.x-t.x)*(e.y-t.y)-(e.x-t.x)*(n.y-t.y))/2)},Bp.signedArea=function(t,e,n){return((n.x-t.x)*(e.y-t.y)-(e.x-t.x)*(n.y-t.y))/2},Bp.det=function(t,e,n,r){return t*r-e*n},Bp.interpolateZ=function(t,e,n,r){var i=e.x,o=e.y,s=n.x-i,a=r.x-i,u=n.y-o,l=r.y-o,c=s*l-a*u,h=t.x-i,p=t.y-o,f=(l*h-a*p)/c,g=(-u*h+s*p)/c;return e.z+f*(n.z-e.z)+g*(r.z-e.z)},Bp.longestSideLength=function(t,e,n){var r=t.distance(e),i=e.distance(n),o=n.distance(t),s=r;return i>s&&(s=i),o>s&&(s=o),s},Bp.isAcute=function(t,e,n){return!!Rp.isAcute(t,e,n)&&(!!Rp.isAcute(e,n,t)&&!!Rp.isAcute(n,t,e))},Bp.circumcentre=function(t,e,n){var r=n.x,i=n.y,o=t.x-r,s=t.y-i,a=e.x-r,u=e.y-i,l=2*Bp.det(o,s,a,u),c=Bp.det(s,o*o+s*s,u,a*a+u*u),h=Bp.det(o,o*o+s*s,a,a*a+u*u);return new ul(r-c/l,i+h/l)},Bp.perpendicularBisector=function(t,e){var n=e.x-t.x,r=e.y-t.y,i=new Ml(t.x+n/2,t.y+r/2,1),o=new Ml(t.x-r+n/2,t.y+n+r/2,1);return new Ml(i,o)},Bp.angleBisector=function(t,e,n){var r=e.distance(t),i=r/(r+e.distance(n)),o=n.x-t.x,s=n.y-t.y;return new ul(t.x+i*o,t.y+i*s)},Bp.area3D=function(t,e,n){var r=e.x-t.x,i=e.y-t.y,o=e.z-t.z,s=n.x-t.x,a=n.y-t.y,u=n.z-t.z,l=i*u-o*a,c=o*s-r*u,h=r*a-i*s,p=l*l+c*c+h*h,f=Math.sqrt(p)/2;return f},Bp.centroid=function(t,e,n){var r=(t.x+e.x+n.x)/3,i=(t.y+e.y+n.y)/3;return new ul(r,i)},Bp.inCentre=function(t,e,n){var r=e.distance(n),i=t.distance(n),o=t.distance(e),s=r+i+o,a=(r*t.x+i*e.x+o*n.x)/s,u=(r*t.y+i*e.y+o*n.y)/s;return new ul(a,u)};var zp=function(){this._inputGeom=null,this._distance=null,this._curveBuilder=null,this._curveList=new lc;var t=arguments[0],e=arguments[1],n=arguments[2];this._inputGeom=t,this._distance=e,this._curveBuilder=n};zp.prototype.addPoint=function(t){if(this._distance<=0)return null;var e=t.getCoordinates(),n=this._curveBuilder.getLineCurve(e,this._distance);this.addCurve(n,pl.EXTERIOR,pl.INTERIOR)},zp.prototype.addPolygon=function(t){var e=this,n=this._distance,r=Sh.LEFT;this._distance<0&&(n=-this._distance,r=Sh.RIGHT);var i=t.getExteriorRing(),o=pc.removeRepeatedPoints(i.getCoordinates());if(this._distance<0&&this.isErodedCompletely(i,this._distance))return null;if(this._distance<=0&&o.length<3)return null;this.addPolygonRing(o,n,r,pl.EXTERIOR,pl.INTERIOR);for(var s=0;s0&&e.isErodedCompletely(a,-e._distance)||e.addPolygonRing(u,n,Sh.opposite(r),pl.INTERIOR,pl.EXTERIOR)}},zp.prototype.isTriangleErodedCompletely=function(t,e){var n=new Bp(t[0],t[1],t[2]),r=n.inCentre();return Xl.distancePointLine(r,n.p0,n.p1)=nh.MINIMUM_VALID_SIZE&&Xl.isCCW(t)&&(o=i,s=r,n=Sh.opposite(n));var a=this._curveBuilder.getRingCurve(t,n,e);this.addCurve(a,o,s)},zp.prototype.add=function(t){if(t.isEmpty())return null;t instanceof th?this.addPolygon(t):t instanceof Zc?this.addLineString(t):t instanceof Qc?this.addPoint(t):(t instanceof eh||t instanceof Gc||t instanceof rh||t instanceof kc)&&this.addCollection(t)},zp.prototype.isErodedCompletely=function(t,e){var n=t.getCoordinates();if(n.length<4)return e<0;if(4===n.length)return this.isTriangleErodedCompletely(n,e);var r=t.getEnvelopeInternal(),i=Math.min(r.getHeight(),r.getWidth());return e<0&&2*Math.abs(e)>i},zp.prototype.addCollection=function(t){for(var e=0;e=this._max)throw new uc;var t=this._parent.getGeometryN(this._index++);return t instanceof kc?(this._subcollectionIterator=new Up(t),this._subcollectionIterator.next()):t},Up.prototype.remove=function(){throw new Error(this.getClass().getName())},Up.prototype.hasNext=function(){if(this._atStart)return!0;if(null!==this._subcollectionIterator){if(this._subcollectionIterator.hasNext())return!0;this._subcollectionIterator=null}return!(this._index>=this._max)},Up.prototype.interfaces_=function(){return[sc]},Up.prototype.getClass=function(){return Up},Up.isAtomic=function(t){return!(t instanceof kc)};var Vp=function(){this._geom=null;var t=arguments[0];this._geom=t};Vp.prototype.locate=function(t){return Vp.locate(t,this._geom)},Vp.prototype.interfaces_=function(){return[jp]},Vp.prototype.getClass=function(){return Vp},Vp.isPointInRing=function(t,e){return!!e.getEnvelopeInternal().intersects(t)&&Xl.isPointInRing(t,e.getCoordinates())},Vp.containsPointInPolygon=function(t,e){if(e.isEmpty())return!1;var n=e.getExteriorRing();if(!Vp.isPointInRing(t,n))return!1;for(var r=0;r=0;n--){var r=this._edgeList.get(n),i=r.getSym();null===e&&(e=i),null!==t&&i.setNext(t),t=r}e.setNext(t)},e.prototype.computeDepths=function(){var t=this;if(1===arguments.length){var e=arguments[0],n=this.findIndex(e),r=e.getDepth(Sh.LEFT),i=e.getDepth(Sh.RIGHT),o=this.computeDepths(n+1,this._edgeList.size(),r),s=this.computeDepths(0,n,o);if(s!==i)throw new Oh("depth mismatch at "+e.getCoordinate())}else if(3===arguments.length){for(var a=arguments[0],u=arguments[1],l=arguments[2],c=l,h=a;h=0;o--){var s=e._resultAreaEdgeList.get(o),a=s.getSym();switch(null===n&&s.getEdgeRing()===t&&(n=s),i){case e._SCANNING_FOR_INCOMING:if(a.getEdgeRing()!==t)continue;r=a,i=e._LINKING_TO_OUTGOING;break;case e._LINKING_TO_OUTGOING:if(s.getEdgeRing()!==t)continue;r.setNextMin(s),i=e._SCANNING_FOR_INCOMING}}i===this._LINKING_TO_OUTGOING&&(ql.isTrue(null!==n,"found null for first outgoing dirEdge"),ql.isTrue(n.getEdgeRing()===t,"unable to link last incoming dirEdge"),r.setNextMin(n))},e.prototype.getOutgoingDegree=function(){if(0===arguments.length){for(var t=0,e=this.iterator();e.hasNext();){var n=e.next();n.isInResult()&&t++}return t}if(1===arguments.length){for(var r=arguments[0],i=0,o=this.iterator();o.hasNext();){var s=o.next();s.getEdgeRing()===r&&i++}return i}},e.prototype.getLabel=function(){return this._label},e.prototype.findCoveredLineEdges=function(){for(var t=pl.NONE,e=this.iterator();e.hasNext();){var n=e.next(),r=n.getSym();if(!n.isLineEdge()){if(n.isInResult()){t=pl.INTERIOR;break}if(r.isInResult()){t=pl.EXTERIOR;break}}}if(t===pl.NONE)return null;for(var i=t,o=this.iterator();o.hasNext();){var s=o.next(),a=s.getSym();s.isLineEdge()?s.getEdge().setCovered(i===pl.INTERIOR):(s.isInResult()&&(i=pl.EXTERIOR),a.isInResult()&&(i=pl.INTERIOR))}},e.prototype.computeLabelling=function(e){t.prototype.computeLabelling.call(this,e),this._label=new Dh(pl.NONE);for(var n=this.iterator();n.hasNext();)for(var r=n.next().getEdge().getLabel(),i=0;i<2;i++){var o=r.getLocation(i);o!==pl.INTERIOR&&o!==pl.BOUNDARY||this._label.setLocation(i,pl.INTERIOR)}},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(Xp),Hp=function(t){function e(){t.apply(this,arguments)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.createNode=function(t){return new Bh(t,new Yp)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(Yh),Wp=function t(){this._pts=null,this._orientation=null;var e=arguments[0];this._pts=e,this._orientation=t.orientation(e)};Wp.prototype.compareTo=function(t){var e=t;return Wp.compareOriented(this._pts,this._orientation,e._pts,e._orientation)},Wp.prototype.interfaces_=function(){return[il]},Wp.prototype.getClass=function(){return Wp},Wp.orientation=function(t){return 1===pc.increasingDirection(t)},Wp.compareOriented=function(t,e,n,r){for(var i=e?1:-1,o=r?1:-1,s=e?t.length:-1,a=r?n.length:-1,u=e?0:t.length-1,l=r?0:n.length-1;;){var c=t[u].compareTo(n[l]);if(0!==c)return c;var h=(u+=i)===s,p=(l+=o)===a;if(h&&!p)return-1;if(!h&&p)return 1;if(h&&p)return 0}};var Jp=function(){this._edges=new lc,this._ocaMap=new Cc};Jp.prototype.print=function(t){t.print("MULTILINESTRING ( ");for(var e=0;e0&&t.print(","),t.print("(");for(var r=n.getCoordinates(),i=0;i0&&t.print(","),t.print(r[i].x+" "+r[i].y);t.println(")")}t.print(") ")},Jp.prototype.addAll=function(t){for(var e=t.iterator();e.hasNext();)this.add(e.next())},Jp.prototype.findEdgeIndex=function(t){for(var e=0;e0||!e.coord.equals2D(r);i||n--;var o=new Array(n).fill(null),s=0;o[s++]=new ul(t.coord);for(var a=t.segmentIndex+1;a<=e.segmentIndex;a++)o[s++]=this.edge.pts[a];return i&&(o[s]=e.coord),new of(o,new Dh(this.edge._label))},$p.prototype.add=function(t,e,n){var r=new Qp(t,e,n),i=this._nodeMap.get(r);return null!==i?i:(this._nodeMap.put(r,r),r)},$p.prototype.isIntersection=function(t){for(var e=this.iterator();e.hasNext();){if(e.next().coord.equals(t))return!0}return!1},$p.prototype.interfaces_=function(){return[]},$p.prototype.getClass=function(){return $p};var tf=function(){};tf.prototype.getChainStartIndices=function(t){var e=0,n=new lc;n.add(new _l(e));do{var r=this.findChainEnd(t,e);n.add(new _l(r)),e=r}while(en?e:n},ef.prototype.getMinX=function(t){var e=this.pts[this.startIndex[t]].x,n=this.pts[this.startIndex[t+1]].x;return en&&(i=1),t._depth[e][r]=i}}},nf.prototype.getDelta=function(t){return this._depth[t][Sh.RIGHT]-this._depth[t][Sh.LEFT]},nf.prototype.getLocation=function(t,e){return this._depth[t][e]<=0?pl.EXTERIOR:pl.INTERIOR},nf.prototype.toString=function(){return"A: "+this._depth[0][1]+","+this._depth[0][2]+" B: "+this._depth[1][1]+","+this._depth[1][2]},nf.prototype.add=function(){var t=this;if(1===arguments.length)for(var e=arguments[0],n=0;n<2;n++)for(var r=1;r<3;r++){var i=e.getLocation(n,r);i!==pl.EXTERIOR&&i!==pl.INTERIOR||(t.isNull(n,r)?t._depth[n][r]=nf.depthAtLocation(i):t._depth[n][r]+=nf.depthAtLocation(i))}else if(3===arguments.length){var o=arguments[0],s=arguments[1],a=arguments[2];a===pl.INTERIOR&&this._depth[o][s]++}},nf.prototype.interfaces_=function(){return[]},nf.prototype.getClass=function(){return nf},nf.depthAtLocation=function(t){return t===pl.EXTERIOR?0:t===pl.INTERIOR?1:nf.NULL_VALUE},rf.NULL_VALUE.get=function(){return-1},Object.defineProperties(nf,rf);var of=function(t){function e(){if(t.call(this),this.pts=null,this._env=null,this.eiList=new $p(this),this._name=null,this._mce=null,this._isIsolated=!0,this._depth=new nf,this._depthDelta=0,1===arguments.length){var n=arguments[0];e.call(this,n,null)}else if(2===arguments.length){var r=arguments[0],i=arguments[1];this.pts=r,this._label=i}}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.getDepth=function(){return this._depth},e.prototype.getCollapsedEdge=function(){var t=new Array(2).fill(null);return t[0]=this.pts[0],t[1]=this.pts[1],new e(t,Dh.toLineLabel(this._label))},e.prototype.isIsolated=function(){return this._isIsolated},e.prototype.getCoordinates=function(){return this.pts},e.prototype.setIsolated=function(t){this._isIsolated=t},e.prototype.setName=function(t){this._name=t},e.prototype.equals=function(t){if(!(t instanceof e))return!1;var n=t;if(this.pts.length!==n.pts.length)return!1;for(var r=!0,i=!0,o=this.pts.length,s=0;s0?this.pts[0]:null;if(1===arguments.length){var t=arguments[0];return this.pts[t]}},e.prototype.print=function(t){t.print("edge "+this._name+": "),t.print("LINESTRING (");for(var e=0;e0&&t.print(","),t.print(this.pts[e].x+" "+this.pts[e].y);t.print(") "+this._label+" "+this._depthDelta)},e.prototype.computeIM=function(t){e.updateIM(this._label,t)},e.prototype.isCollapsed=function(){return!!this._label.isArea()&&(3===this.pts.length&&!!this.pts[0].equals(this.pts[2]))},e.prototype.isClosed=function(){return this.pts[0].equals(this.pts[this.pts.length-1])},e.prototype.getMaximumSegmentIndex=function(){return this.pts.length-1},e.prototype.getDepthDelta=function(){return this._depthDelta},e.prototype.getNumPoints=function(){return this.pts.length},e.prototype.printReverse=function(t){t.print("edge "+this._name+": ");for(var e=this.pts.length-1;e>=0;e--)t.print(this.pts[e]+" ");t.println("")},e.prototype.getMonotoneChainEdge=function(){return null===this._mce&&(this._mce=new ef(this)),this._mce},e.prototype.getEnvelope=function(){if(null===this._env){this._env=new Ll;for(var t=0;t0&&t.append(","),t.append(this.pts[e].x+" "+this.pts[e].y);return t.append(") "+this._label+" "+this._depthDelta),t.toString()},e.prototype.isPointwiseEqual=function(t){if(this.pts.length!==t.pts.length)return!1;for(var e=0;er||this._maxyo;if(s)return!1;var a=this.intersectsToleranceSquare(t,e);return ql.isTrue(!(s&&a),"Found bad envelope test"),a},cf.prototype.initCorners=function(t){var e=.5;this._minx=t.x-e,this._maxx=t.x+e,this._miny=t.y-e,this._maxy=t.y+e,this._corner[0]=new ul(this._maxx,this._maxy),this._corner[1]=new ul(this._minx,this._maxy),this._corner[2]=new ul(this._minx,this._miny),this._corner[3]=new ul(this._maxx,this._miny)},cf.prototype.intersects=function(t,e){return 1===this._scaleFactor?this.intersectsScaled(t,e):(this.copyScaled(t,this._p0Scaled),this.copyScaled(e,this._p1Scaled),this.intersectsScaled(this._p0Scaled,this._p1Scaled))},cf.prototype.scale=function(t){return Math.round(t*this._scaleFactor)},cf.prototype.getCoordinate=function(){return this._originalPt},cf.prototype.copyScaled=function(t,e){e.x=this.scale(t.x),e.y=this.scale(t.y)},cf.prototype.getSafeEnvelope=function(){if(null===this._safeEnv){var t=cf.SAFE_ENV_EXPANSION_FACTOR/this._scaleFactor;this._safeEnv=new Ll(this._originalPt.x-t,this._originalPt.x+t,this._originalPt.y-t,this._originalPt.y+t)}return this._safeEnv},cf.prototype.intersectsPixelClosure=function(t,e){return this._li.computeIntersection(t,e,this._corner[0],this._corner[1]),!!this._li.hasIntersection()||(this._li.computeIntersection(t,e,this._corner[1],this._corner[2]),!!this._li.hasIntersection()||(this._li.computeIntersection(t,e,this._corner[2],this._corner[3]),!!this._li.hasIntersection()||(this._li.computeIntersection(t,e,this._corner[3],this._corner[0]),!!this._li.hasIntersection())))},cf.prototype.intersectsToleranceSquare=function(t,e){var n=!1,r=!1;return this._li.computeIntersection(t,e,this._corner[0],this._corner[1]),!!this._li.isProper()||(this._li.computeIntersection(t,e,this._corner[1],this._corner[2]),!!this._li.isProper()||(this._li.hasIntersection()&&(n=!0),this._li.computeIntersection(t,e,this._corner[2],this._corner[3]),!!this._li.isProper()||(this._li.hasIntersection()&&(r=!0),this._li.computeIntersection(t,e,this._corner[3],this._corner[0]),!!this._li.isProper()||(!(!n||!r)||(!!t.equals(this._pt)||!!e.equals(this._pt))))))},cf.prototype.addSnappedNode=function(t,e){var n=t.getCoordinate(e),r=t.getCoordinate(e+1);return!!this.intersects(n,r)&&(t.addIntersection(this.getCoordinate(),e),!0)},cf.prototype.interfaces_=function(){return[]},cf.prototype.getClass=function(){return cf},hf.SAFE_ENV_EXPANSION_FACTOR.get=function(){return.75},Object.defineProperties(cf,hf);var pf=function(){this.tempEnv1=new Ll,this.selectedSegment=new vp};pf.prototype.select=function(){if(1===arguments.length);else if(2===arguments.length){var t=arguments[0],e=arguments[1];t.getLineSegment(e,this.selectedSegment),this.select(this.selectedSegment)}},pf.prototype.interfaces_=function(){return[]},pf.prototype.getClass=function(){return pf};var ff=function(){this._index=null;var t=arguments[0];this._index=t},gf={HotPixelSnapAction:{configurable:!0}};ff.prototype.snap=function(){if(1===arguments.length){var t=arguments[0];return this.snap(t,null,-1)}if(3===arguments.length){var e=arguments[0],n=arguments[1],r=arguments[2],i=e.getSafeEnvelope(),o=new df(e,n,r);return this._index.query(i,{interfaces_:function(){return[Qh]},visitItem:function(t){t.select(i,o)}}),o.isNodeAdded()}},ff.prototype.interfaces_=function(){return[]},ff.prototype.getClass=function(){return ff},gf.HotPixelSnapAction.get=function(){return df},Object.defineProperties(ff,gf);var df=function(t){function e(){t.call(this),this._hotPixel=null,this._parentEdge=null,this._hotPixelVertexIndex=null,this._isNodeAdded=!1;var e=arguments[0],n=arguments[1],r=arguments[2];this._hotPixel=e,this._parentEdge=n,this._hotPixelVertexIndex=r}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.isNodeAdded=function(){return this._isNodeAdded},e.prototype.select=function(){if(2!==arguments.length)return t.prototype.select.apply(this,arguments);var e=arguments[0],n=arguments[1],r=e.getContext();if(null!==this._parentEdge&&r===this._parentEdge&&n===this._hotPixelVertexIndex)return null;this._isNodeAdded=this._hotPixel.addSnappedNode(r,n)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(pf),yf=function(){this._li=null,this._interiorIntersections=null;var t=arguments[0];this._li=t,this._interiorIntersections=new lc};yf.prototype.processIntersections=function(t,e,n,r){if(t===n&&e===r)return null;var i=t.getCoordinates()[e],o=t.getCoordinates()[e+1],s=n.getCoordinates()[r],a=n.getCoordinates()[r+1];if(this._li.computeIntersection(i,o,s,a),this._li.hasIntersection()&&this._li.isInteriorIntersection()){for(var u=0;u=0;e--){try{t.bufferReducedPrecision(e)}catch(e){if(!(e instanceof Oh))throw e;t._saveException=e}if(null!==t._resultGeometry)return null}throw this._saveException}if(1===arguments.length){var n=arguments[0],r=_f.precisionScaleFactor(this._argGeom,this._distance,n),i=new gh(r);this.bufferFixedPrecision(i)}},_f.prototype.computeGeometry=function(){if(this.bufferOriginalPrecision(),null!==this._resultGeometry)return null;var t=this._argGeom.getFactory().getPrecisionModel();t.getType()===gh.FIXED?this.bufferFixedPrecision(t):this.bufferReducedPrecision()},_f.prototype.setQuadrantSegments=function(t){this._bufParams.setQuadrantSegments(t)},_f.prototype.bufferOriginalPrecision=function(){try{var t=new sf(this._bufParams);this._resultGeometry=t.buffer(this._argGeom,this._distance)}catch(t){if(!(t instanceof kl))throw t;this._saveException=t}},_f.prototype.getResultGeometry=function(t){return this._distance=t,this.computeGeometry(),this._resultGeometry},_f.prototype.setEndCapStyle=function(t){this._bufParams.setEndCapStyle(t)},_f.prototype.interfaces_=function(){return[]},_f.prototype.getClass=function(){return _f},_f.bufferOp=function(){if(2===arguments.length){var t=arguments[0],e=arguments[1],n=new _f(t),r=n.getResultGeometry(e);return r}if(3===arguments.length){if(Number.isInteger(arguments[2])&&arguments[0]instanceof Wl&&"number"==typeof arguments[1]){var i=arguments[0],o=arguments[1],s=arguments[2],a=new _f(i);a.setQuadrantSegments(s);var u=a.getResultGeometry(o);return u}if(arguments[2]instanceof Sp&&arguments[0]instanceof Wl&&"number"==typeof arguments[1]){var l=arguments[0],c=arguments[1],h=arguments[2],p=new _f(l,h),f=p.getResultGeometry(c);return f}}else if(4===arguments.length){var g=arguments[0],d=arguments[1],y=arguments[2],v=arguments[3],_=new _f(g);_.setQuadrantSegments(y),_.setEndCapStyle(v);var m=_.getResultGeometry(d);return m}},_f.precisionScaleFactor=function(t,e,n){var r=t.getEnvelopeInternal(),i=dl.max(Math.abs(r.getMaxX()),Math.abs(r.getMaxY()),Math.abs(r.getMinX()),Math.abs(r.getMinY()))+2*(e>0?e:0),o=n-Math.trunc(Math.log(i)/Math.log(10)+1);return Math.pow(10,o)},mf.CAP_ROUND.get=function(){return Sp.CAP_ROUND},mf.CAP_BUTT.get=function(){return Sp.CAP_FLAT},mf.CAP_FLAT.get=function(){return Sp.CAP_FLAT},mf.CAP_SQUARE.get=function(){return Sp.CAP_SQUARE},mf.MAX_PRECISION_DIGITS.get=function(){return 12},Object.defineProperties(_f,mf);var xf=function(){this._pt=[new ul,new ul],this._distance=nl.NaN,this._isNull=!0};xf.prototype.getCoordinates=function(){return this._pt},xf.prototype.getCoordinate=function(t){return this._pt[t]},xf.prototype.setMinimum=function(){if(1===arguments.length){var t=arguments[0];this.setMinimum(t._pt[0],t._pt[1])}else if(2===arguments.length){var e=arguments[0],n=arguments[1];if(this._isNull)return this.initialize(e,n),null;var r=e.distance(n);rthis._distance&&this.initialize(e,n,r)}},xf.prototype.interfaces_=function(){return[]},xf.prototype.getClass=function(){return xf};var Ef=function(){};Ef.prototype.interfaces_=function(){return[]},Ef.prototype.getClass=function(){return Ef},Ef.computeDistance=function(){if(arguments[2]instanceof xf&&arguments[0]instanceof Zc&&arguments[1]instanceof ul)for(var t=arguments[0],e=arguments[1],n=arguments[2],r=t.getCoordinates(),i=new vp,o=0;o0||this._isIn?pl.INTERIOR:pl.EXTERIOR)},Pf.prototype.interfaces_=function(){return[]},Pf.prototype.getClass=function(){return Pf};var Mf=function t(){if(this._component=null,this._segIndex=null,this._pt=null,2===arguments.length){var e=arguments[0],n=arguments[1];t.call(this,e,t.INSIDE_AREA,n)}else if(3===arguments.length){var r=arguments[0],i=arguments[1],o=arguments[2];this._component=r,this._segIndex=i,this._pt=o}},Lf={INSIDE_AREA:{configurable:!0}};Mf.prototype.isInsideArea=function(){return this._segIndex===Mf.INSIDE_AREA},Mf.prototype.getCoordinate=function(){return this._pt},Mf.prototype.getGeometryComponent=function(){return this._component},Mf.prototype.getSegmentIndex=function(){return this._segIndex},Mf.prototype.interfaces_=function(){return[]},Mf.prototype.getClass=function(){return Mf},Lf.INSIDE_AREA.get=function(){return-1},Object.defineProperties(Mf,Lf);var Of=function(t){this._pts=t||null};Of.prototype.filter=function(t){t instanceof Qc&&this._pts.add(t)},Of.prototype.interfaces_=function(){return[Dc]},Of.prototype.getClass=function(){return Of},Of.getPoints=function(){if(1===arguments.length){var t=arguments[0];return t instanceof Qc?np.singletonList(t):Of.getPoints(t,new lc)}if(2===arguments.length){var e=arguments[0],n=arguments[1];return e instanceof Qc?n.add(e):e instanceof kc&&e.apply(new Of(n)),n}};var Rf=function(){this._locations=null;var t=arguments[0];this._locations=t};Rf.prototype.filter=function(t){(t instanceof Qc||t instanceof Zc||t instanceof th)&&this._locations.add(new Mf(t,0,t.getCoordinate()))},Rf.prototype.interfaces_=function(){return[Dc]},Rf.prototype.getClass=function(){return Rf},Rf.getLocations=function(t){var e=new lc;return t.apply(new Rf(e)),e};var Tf=function(){if(this._geom=null,this._terminateDistance=0,this._ptLocator=new Pf,this._minDistanceLocation=null,this._minDistance=nl.MAX_VALUE,2===arguments.length){var t=arguments[0],e=arguments[1];this._geom=[t,e],this._terminateDistance=0}else if(3===arguments.length){var n=arguments[0],r=arguments[1],i=arguments[2];this._geom=new Array(2).fill(null),this._geom[0]=n,this._geom[1]=r,this._terminateDistance=i}};Tf.prototype.computeContainmentDistance=function(){var t=this;if(0===arguments.length){var e=new Array(2).fill(null);if(this.computeContainmentDistance(0,e),this._minDistance<=this._terminateDistance)return null;this.computeContainmentDistance(1,e)}else if(2===arguments.length){var n=arguments[0],r=arguments[1],i=1-n,o=Sf.getPolygons(this._geom[n]);if(o.size()>0){var s=Rf.getLocations(this._geom[i]);if(this.computeContainmentDistance(s,o,r),this._minDistance<=this._terminateDistance)return this._minDistanceLocation[i]=r[0],this._minDistanceLocation[n]=r[1],null}}else if(3===arguments.length)if(arguments[2]instanceof Array&&gl(arguments[0],ac)&&gl(arguments[1],ac)){for(var a=arguments[0],u=arguments[1],l=arguments[2],c=0;cthis._minDistance)return null;for(var i=e.getCoordinates(),o=n.getCoordinate(),s=0;sthis._minDistance)return null;for(var f=c.getCoordinates(),g=h.getCoordinates(),d=0;dthis._distance&&this.initialize(e,n,r)}},Af.prototype.interfaces_=function(){return[]},Af.prototype.getClass=function(){return Af};var Df=function(){};Df.prototype.interfaces_=function(){return[]},Df.prototype.getClass=function(){return Df},Df.computeDistance=function(){if(arguments[2]instanceof Af&&arguments[0]instanceof Zc&&arguments[1]instanceof ul)for(var t=arguments[0],e=arguments[1],n=arguments[2],r=new vp,i=t.getCoordinates(),o=0;o1||t<=0)throw new el("Fraction is not in range (0.0 - 1.0]");this._densifyFrac=t},Ff.prototype.compute=function(t,e){this.computeOrientedDistance(t,e,this._ptDist),this.computeOrientedDistance(e,t,this._ptDist)},Ff.prototype.distance=function(){return this.compute(this._g0,this._g1),this._ptDist.getDistance()},Ff.prototype.computeOrientedDistance=function(t,e,n){var r=new Gf(e);if(t.apply(r),n.setMaximum(r.getMaxPointDistance()),this._densifyFrac>0){var i=new qf(e,this._densifyFrac);t.apply(i),n.setMaximum(i.getMaxPointDistance())}},Ff.prototype.orientedDistance=function(){return this.computeOrientedDistance(this._g0,this._g1,this._ptDist),this._ptDist.getDistance()},Ff.prototype.interfaces_=function(){return[]},Ff.prototype.getClass=function(){return Ff},Ff.distance=function(){if(2===arguments.length){var t=arguments[0],e=arguments[1],n=new Ff(t,e);return n.distance()}if(3===arguments.length){var r=arguments[0],i=arguments[1],o=arguments[2],s=new Ff(r,i);return s.setDensifyFraction(o),s.distance()}},kf.MaxPointDistanceFilter.get=function(){return Gf},kf.MaxDensifiedByFractionDistanceFilter.get=function(){return qf},Object.defineProperties(Ff,kf);var Gf=function(){this._maxPtDist=new Af,this._minPtDist=new Af,this._euclideanDist=new Df,this._geom=null;var t=arguments[0];this._geom=t};Gf.prototype.filter=function(t){this._minPtDist.initialize(),Df.computeDistance(this._geom,t,this._minPtDist),this._maxPtDist.setMaximum(this._minPtDist)},Gf.prototype.getMaxPointDistance=function(){return this._maxPtDist},Gf.prototype.interfaces_=function(){return[Kl]},Gf.prototype.getClass=function(){return Gf};var qf=function(){this._maxPtDist=new Af,this._minPtDist=new Af,this._geom=null,this._numSubSegs=0;var t=arguments[0],e=arguments[1];this._geom=t,this._numSubSegs=Math.trunc(Math.round(1/e))};qf.prototype.filter=function(t,e){var n=this;if(0===e)return null;for(var r=t.getCoordinate(e-1),i=t.getCoordinate(e),o=(i.x-r.x)/this._numSubSegs,s=(i.y-r.y)/this._numSubSegs,a=0;an){this._isValid=!1;var i=r.getCoordinates();this._errorLocation=i[1],this._errorIndicator=t.getFactory().createLineString(i),this._errMsg="Distance between buffer curve and input is too large ("+this._maxDistanceFound+" at "+Fl.toLineString(i[0],i[1])+")"}},Bf.prototype.isValid=function(){var t=Math.abs(this._bufDistance),e=Bf.MAX_DISTANCE_DIFF_FRAC*t;return this._minValidDistance=t-e,this._maxValidDistance=t+e,!(!this._input.isEmpty()&&!this._result.isEmpty())||(this._bufDistance>0?this.checkPositiveValid():this.checkNegativeValid(),Bf.VERBOSE&&Pl.out.println("Min Dist= "+this._minDistanceFound+" err= "+(1-this._minDistanceFound/this._bufDistance)+" Max Dist= "+this._maxDistanceFound+" err= "+(this._maxDistanceFound/this._bufDistance-1)),this._isValid)},Bf.prototype.checkNegativeValid=function(){if(!(this._input instanceof th||this._input instanceof rh||this._input instanceof kc))return null;var t=this.getPolygonLines(this._input);if(this.checkMinimumDistance(t,this._result,this._minValidDistance),!this._isValid)return null;this.checkMaximumDistance(t,this._result,this._maxValidDistance)},Bf.prototype.getErrorIndicator=function(){return this._errorIndicator},Bf.prototype.checkMinimumDistance=function(t,e,n){var r=new Tf(t,e,n);if(this._minDistanceFound=r.distance(),this._minDistanceFound0&&t>e&&(this._isValid=!1,this._errorMsg="Area of positive buffer is smaller than input",this._errorIndicator=this._result),this._distance<0&&t=2||this._distance>0?null:(this._result.isEmpty()||(this._isValid=!1,this._errorMsg="Result is non-empty",this._errorIndicator=this._result),void this.report("ExpectedEmpty"))},jf.prototype.report=function(t){if(!jf.VERBOSE)return null;Pl.out.println("Check "+t+": "+(this._isValid?"passed":"FAILED"))},jf.prototype.getErrorMessage=function(){return this._errorMsg},jf.prototype.interfaces_=function(){return[]},jf.prototype.getClass=function(){return jf},jf.isValidMsg=function(t,e,n){var r=new jf(t,e,n);return r.isValid()?null:r.getErrorMessage()},jf.isValid=function(t,e,n){return!!new jf(t,e,n).isValid()},Uf.VERBOSE.get=function(){return!1},Uf.MAX_ENV_DIFF_FRAC.get=function(){return.012},Object.defineProperties(jf,Uf);var Vf=function(){this._pts=null,this._data=null;var t=arguments[0],e=arguments[1];this._pts=t,this._data=e};Vf.prototype.getCoordinates=function(){return this._pts},Vf.prototype.size=function(){return this._pts.length},Vf.prototype.getCoordinate=function(t){return this._pts[t]},Vf.prototype.isClosed=function(){return this._pts[0].equals(this._pts[this._pts.length-1])},Vf.prototype.getSegmentOctant=function(t){return t===this._pts.length-1?-1:fp.octant(this.getCoordinate(t),this.getCoordinate(t+1))},Vf.prototype.setData=function(t){this._data=t},Vf.prototype.getData=function(){return this._data},Vf.prototype.toString=function(){return Fl.toLineString(new lh(this._pts))},Vf.prototype.interfaces_=function(){return[gp]},Vf.prototype.getClass=function(){return Vf};var Xf=function(){this._findAllIntersections=!1,this._isCheckEndSegmentsOnly=!1,this._li=null,this._interiorIntersection=null,this._intSegments=null,this._intersections=new lc,this._intersectionCount=0,this._keepIntersections=!0;var t=arguments[0];this._li=t,this._interiorIntersection=null};Xf.prototype.getInteriorIntersection=function(){return this._interiorIntersection},Xf.prototype.setCheckEndSegmentsOnly=function(t){this._isCheckEndSegmentsOnly=t},Xf.prototype.getIntersectionSegments=function(){return this._intSegments},Xf.prototype.count=function(){return this._intersectionCount},Xf.prototype.getIntersections=function(){return this._intersections},Xf.prototype.setFindAllIntersections=function(t){this._findAllIntersections=t},Xf.prototype.setKeepIntersections=function(t){this._keepIntersections=t},Xf.prototype.processIntersections=function(t,e,n,r){if(!this._findAllIntersections&&this.hasIntersection())return null;if(t===n&&e===r)return null;if(this._isCheckEndSegmentsOnly&&!(this.isEndSegment(t,e)||this.isEndSegment(n,r)))return null;var i=t.getCoordinates()[e],o=t.getCoordinates()[e+1],s=n.getCoordinates()[r],a=n.getCoordinates()[r+1];this._li.computeIntersection(i,o,s,a),this._li.hasIntersection()&&this._li.isInteriorIntersection()&&(this._intSegments=new Array(4).fill(null),this._intSegments[0]=i,this._intSegments[1]=o,this._intSegments[2]=s,this._intSegments[3]=a,this._interiorIntersection=this._li.getIntersection(0),this._keepIntersections&&this._intersections.add(this._interiorIntersection),this._intersectionCount++)},Xf.prototype.isEndSegment=function(t,e){return 0===e||e>=t.size()-2},Xf.prototype.hasIntersection=function(){return null!==this._interiorIntersection},Xf.prototype.isDone=function(){return!this._findAllIntersections&&null!==this._interiorIntersection},Xf.prototype.interfaces_=function(){return[Zp]},Xf.prototype.getClass=function(){return Xf},Xf.createAllIntersectionsFinder=function(t){var e=new Xf(t);return e.setFindAllIntersections(!0),e},Xf.createAnyIntersectionFinder=function(t){return new Xf(t)},Xf.createIntersectionCounter=function(t){var e=new Xf(t);return e.setFindAllIntersections(!0),e.setKeepIntersections(!1),e};var Yf=function(){this._li=new jl,this._segStrings=null,this._findAllIntersections=!1,this._segInt=null,this._isValid=!0;var t=arguments[0];this._segStrings=t};Yf.prototype.execute=function(){if(null!==this._segInt)return null;this.checkInteriorIntersections()},Yf.prototype.getIntersections=function(){return this._segInt.getIntersections()},Yf.prototype.isValid=function(){return this.execute(),this._isValid},Yf.prototype.setFindAllIntersections=function(t){this._findAllIntersections=t},Yf.prototype.checkInteriorIntersections=function(){this._isValid=!0,this._segInt=new Xf(this._li),this._segInt.setFindAllIntersections(this._findAllIntersections);var t=new Ip;if(t.setSegmentIntersector(this._segInt),t.computeNodes(this._segStrings),this._segInt.hasIntersection())return this._isValid=!1,null},Yf.prototype.checkValid=function(){if(this.execute(),!this._isValid)throw new Oh(this.getErrorMessage(),this._segInt.getInteriorIntersection())},Yf.prototype.getErrorMessage=function(){if(this._isValid)return"no intersections found";var t=this._segInt.getIntersectionSegments();return"found non-noded intersection between "+Fl.toLineString(t[0],t[1])+" and "+Fl.toLineString(t[2],t[3])},Yf.prototype.interfaces_=function(){return[]},Yf.prototype.getClass=function(){return Yf},Yf.computeIntersections=function(t){var e=new Yf(t);return e.setFindAllIntersections(!0),e.isValid(),e.getIntersections()};var Hf=function t(){this._nv=null;var e=arguments[0];this._nv=new Yf(t.toSegmentStrings(e))};Hf.prototype.checkValid=function(){this._nv.checkValid()},Hf.prototype.interfaces_=function(){return[]},Hf.prototype.getClass=function(){return Hf},Hf.toSegmentStrings=function(t){for(var e=new lc,n=t.iterator();n.hasNext();){var r=n.next();e.add(new Vf(r.getCoordinates(),r))}return e},Hf.checkValid=function(t){new Hf(t).checkValid()};var Wf=function(t){this._mapOp=t};Wf.prototype.map=function(t){for(var e=new lc,n=0;n0&&r<4&&!this._preserveType?this._factory.createLineString(n):this._factory.createLinearRing(n)},Kf.prototype.interfaces_=function(){return[]},Kf.prototype.getClass=function(){return Kf};var Qf=function t(){if(this._snapTolerance=0,this._srcPts=null,this._seg=new vp,this._allowSnappingToSourceVertices=!1,this._isClosed=!1,arguments[0]instanceof Zc&&"number"==typeof arguments[1]){var e=arguments[0],n=arguments[1];t.call(this,e.getCoordinates(),n)}else if(arguments[0]instanceof Array&&"number"==typeof arguments[1]){var r=arguments[0],i=arguments[1];this._srcPts=r,this._isClosed=t.isClosed(r),this._snapTolerance=i}};Qf.prototype.snapVertices=function(t,e){for(var n=this._isClosed?t.size()-1:t.size(),r=0;r=0&&t.add(o+1,new ul(i),!1)}},Qf.prototype.findSegmentIndexToSnap=function(t,e){for(var n=this,r=nl.MAX_VALUE,i=-1,o=0;oe&&(e=r)}return e}if(2===arguments.length){var i=arguments[0],o=arguments[1];return Math.min($f.computeOverlaySnapTolerance(i),$f.computeOverlaySnapTolerance(o))}},$f.computeSizeBasedSnapTolerance=function(t){var e=t.getEnvelopeInternal();return Math.min(e.getHeight(),e.getWidth())*$f.SNAP_PRECISION_FACTOR},$f.snapToSelf=function(t,e,n){return new $f(t).snapToSelf(e,n)},tg.SNAP_PRECISION_FACTOR.get=function(){return 1e-9},Object.defineProperties($f,tg);var eg=function(t){function e(e,n,r){t.call(this),this._snapTolerance=e||null,this._snapPts=n||null,this._isSelfSnap=void 0!==r&&r}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.snapLine=function(t,e){var n=new Qf(t,this._snapTolerance);return n.setAllowSnappingToSourceVertices(this._isSelfSnap),n.snapTo(e)},e.prototype.transformCoordinates=function(t,e){var n=t.toCoordinateArray(),r=this.snapLine(n,this._snapPts);return this._factory.getCoordinateSequenceFactory().create(r)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(Kf),ng=function(){this._isFirst=!0,this._commonMantissaBitsCount=53,this._commonBits=0,this._commonSignExp=null};ng.prototype.getCommon=function(){return nl.longBitsToDouble(this._commonBits)},ng.prototype.add=function(t){var e=nl.doubleToLongBits(t);return this._isFirst?(this._commonBits=e,this._commonSignExp=ng.signExpBits(this._commonBits),this._isFirst=!1,null):ng.signExpBits(e)!==this._commonSignExp?(this._commonBits=0,null):(this._commonMantissaBitsCount=ng.numCommonMostSigMantissaBits(this._commonBits,e),void(this._commonBits=ng.zeroLowerBits(this._commonBits,64-(12+this._commonMantissaBitsCount))))},ng.prototype.toString=function(){if(1===arguments.length){var t=arguments[0],e=nl.longBitsToDouble(t),n=nl.toBinaryString(t),r="0000000000000000000000000000000000000000000000000000000000000000"+n,i=r.substring(r.length-64),o=i.substring(0,1)+" "+i.substring(1,12)+"(exp) "+i.substring(12)+" [ "+e+" ]";return o}},ng.prototype.interfaces_=function(){return[]},ng.prototype.getClass=function(){return ng},ng.getBit=function(t,e){return 0!=(t&1<>52},ng.zeroLowerBits=function(t,e){return t&~((1<=0;r--){if(ng.getBit(t,r)!==ng.getBit(e,r))return n;n++}return 52};var rg=function(){this._commonCoord=null,this._ccFilter=new og},ig={CommonCoordinateFilter:{configurable:!0},Translater:{configurable:!0}};rg.prototype.addCommonBits=function(t){var e=new sg(this._commonCoord);t.apply(e),t.geometryChanged()},rg.prototype.removeCommonBits=function(t){if(0===this._commonCoord.x&&0===this._commonCoord.y)return t;var e=new ul(this._commonCoord);e.x=-e.x,e.y=-e.y;var n=new sg(e);return t.apply(n),t.geometryChanged(),t},rg.prototype.getCommonCoordinate=function(){return this._commonCoord},rg.prototype.add=function(t){t.apply(this._ccFilter),this._commonCoord=this._ccFilter.getCommonCoordinate()},rg.prototype.interfaces_=function(){return[]},rg.prototype.getClass=function(){return rg},ig.CommonCoordinateFilter.get=function(){return og},ig.Translater.get=function(){return sg},Object.defineProperties(rg,ig);var og=function(){this._commonBitsX=new ng,this._commonBitsY=new ng};og.prototype.filter=function(t){this._commonBitsX.add(t.x),this._commonBitsY.add(t.y)},og.prototype.getCommonCoordinate=function(){return new ul(this._commonBitsX.getCommon(),this._commonBitsY.getCommon())},og.prototype.interfaces_=function(){return[Kl]},og.prototype.getClass=function(){return og};var sg=function(){this.trans=null;var t=arguments[0];this.trans=t};sg.prototype.filter=function(t,e){var n=t.getOrdinate(e,0)+this.trans.x,r=t.getOrdinate(e,1)+this.trans.y;t.setOrdinate(e,0,n),t.setOrdinate(e,1,r)},sg.prototype.isDone=function(){return!1},sg.prototype.isGeometryChanged=function(){return!0},sg.prototype.interfaces_=function(){return[Fc]},sg.prototype.getClass=function(){return sg};var ag=function(t,e){this._geom=new Array(2).fill(null),this._snapTolerance=null,this._cbr=null,this._geom[0]=t,this._geom[1]=e,this.computeSnapTolerance()};ag.prototype.selfSnap=function(t){return new $f(t).snapTo(t,this._snapTolerance)},ag.prototype.removeCommonBits=function(t){this._cbr=new rg,this._cbr.add(t[0]),this._cbr.add(t[1]);var e=new Array(2).fill(null);return e[0]=this._cbr.removeCommonBits(t[0].copy()),e[1]=this._cbr.removeCommonBits(t[1].copy()),e},ag.prototype.prepareResult=function(t){return this._cbr.addCommonBits(t),t},ag.prototype.getResultGeometry=function(t){var e=this.snap(this._geom),n=Mg.overlayOp(e[0],e[1],t);return this.prepareResult(n)},ag.prototype.checkValid=function(t){t.isValid()||Pl.out.println("Snapped geometry is invalid")},ag.prototype.computeSnapTolerance=function(){this._snapTolerance=$f.computeOverlaySnapTolerance(this._geom[0],this._geom[1])},ag.prototype.snap=function(t){var e=this.removeCommonBits(t);return $f.snap(e[0],e[1],this._snapTolerance)},ag.prototype.interfaces_=function(){return[]},ag.prototype.getClass=function(){return ag},ag.overlayOp=function(t,e,n){return new ag(t,e).getResultGeometry(n)},ag.union=function(t,e){return ag.overlayOp(t,e,Mg.UNION)},ag.intersection=function(t,e){return ag.overlayOp(t,e,Mg.INTERSECTION)},ag.symDifference=function(t,e){return ag.overlayOp(t,e,Mg.SYMDIFFERENCE)},ag.difference=function(t,e){return ag.overlayOp(t,e,Mg.DIFFERENCE)};var ug=function(t,e){this._geom=new Array(2).fill(null),this._geom[0]=t,this._geom[1]=e};ug.prototype.getResultGeometry=function(t){var e=null,n=!1,r=null;try{e=Mg.overlayOp(this._geom[0],this._geom[1],t);n=!0}catch(t){if(!(t instanceof kl))throw t;r=t}if(!n)try{e=ag.overlayOp(this._geom[0],this._geom[1],t)}catch(t){throw t instanceof kl?r:t}return e},ug.prototype.interfaces_=function(){return[]},ug.prototype.getClass=function(){return ug},ug.overlayOp=function(t,e,n){return new ug(t,e).getResultGeometry(n)},ug.union=function(t,e){return ug.overlayOp(t,e,Mg.UNION)},ug.intersection=function(t,e){return ug.overlayOp(t,e,Mg.INTERSECTION)},ug.symDifference=function(t,e){return ug.overlayOp(t,e,Mg.SYMDIFFERENCE)},ug.difference=function(t,e){return ug.overlayOp(t,e,Mg.DIFFERENCE)};var lg=function(){this.mce=null,this.chainIndex=null;var t=arguments[0],e=arguments[1];this.mce=t,this.chainIndex=e};lg.prototype.computeIntersections=function(t,e){this.mce.computeIntersectsForChain(this.chainIndex,t.mce,t.chainIndex,e)},lg.prototype.interfaces_=function(){return[]},lg.prototype.getClass=function(){return lg};var cg=function t(){if(this._label=null,this._xValue=null,this._eventType=null,this._insertEvent=null,this._deleteEventIndex=null,this._obj=null,2===arguments.length){var e=arguments[0],n=arguments[1];this._eventType=t.DELETE,this._xValue=e,this._insertEvent=n}else if(3===arguments.length){var r=arguments[0],i=arguments[1],o=arguments[2];this._eventType=t.INSERT,this._label=r,this._xValue=i,this._obj=o}},hg={INSERT:{configurable:!0},DELETE:{configurable:!0}};cg.prototype.isDelete=function(){return this._eventType===cg.DELETE},cg.prototype.setDeleteEventIndex=function(t){this._deleteEventIndex=t},cg.prototype.getObject=function(){return this._obj},cg.prototype.compareTo=function(t){var e=t;return this._xValuee._xValue?1:this._eventTypee._eventType?1:0},cg.prototype.getInsertEvent=function(){return this._insertEvent},cg.prototype.isInsert=function(){return this._eventType===cg.INSERT},cg.prototype.isSameLabel=function(t){return null!==this._label&&this._label===t._label},cg.prototype.getDeleteEventIndex=function(){return this._deleteEventIndex},cg.prototype.interfaces_=function(){return[il]},cg.prototype.getClass=function(){return cg},hg.INSERT.get=function(){return 1},hg.DELETE.get=function(){return 2},Object.defineProperties(cg,hg);var pg=function(){};pg.prototype.interfaces_=function(){return[]},pg.prototype.getClass=function(){return pg};var fg=function(){this._hasIntersection=!1,this._hasProper=!1,this._hasProperInterior=!1,this._properIntersectionPoint=null,this._li=null,this._includeProper=null,this._recordIsolated=null,this._isSelfIntersection=null,this._numIntersections=0,this.numTests=0,this._bdyNodes=null,this._isDone=!1,this._isDoneWhenProperInt=!1;var t=arguments[0],e=arguments[1],n=arguments[2];this._li=t,this._includeProper=e,this._recordIsolated=n};fg.prototype.isTrivialIntersection=function(t,e,n,r){if(t===n&&1===this._li.getIntersectionNum()){if(fg.isAdjacentSegments(e,r))return!0;if(t.isClosed()){var i=t.getNumPoints()-1;if(0===e&&r===i||0===r&&e===i)return!0}}return!1},fg.prototype.getProperIntersectionPoint=function(){return this._properIntersectionPoint},fg.prototype.setIsDoneIfProperInt=function(t){this._isDoneWhenProperInt=t},fg.prototype.hasProperInteriorIntersection=function(){return this._hasProperInterior},fg.prototype.isBoundaryPointInternal=function(t,e){for(var n=e.iterator();n.hasNext();){var r=n.next().getCoordinate();if(t.isIntersection(r))return!0}return!1},fg.prototype.hasProperIntersection=function(){return this._hasProper},fg.prototype.hasIntersection=function(){return this._hasIntersection},fg.prototype.isDone=function(){return this._isDone},fg.prototype.isBoundaryPoint=function(t,e){return null!==e&&(!!this.isBoundaryPointInternal(t,e[0])||!!this.isBoundaryPointInternal(t,e[1]))},fg.prototype.setBoundaryNodes=function(t,e){this._bdyNodes=new Array(2).fill(null),this._bdyNodes[0]=t,this._bdyNodes[1]=e},fg.prototype.addIntersections=function(t,e,n,r){if(t===n&&e===r)return null;this.numTests++;var i=t.getCoordinates()[e],o=t.getCoordinates()[e+1],s=n.getCoordinates()[r],a=n.getCoordinates()[r+1];this._li.computeIntersection(i,o,s,a),this._li.hasIntersection()&&(this._recordIsolated&&(t.setIsolated(!1),n.setIsolated(!1)),this._numIntersections++,this.isTrivialIntersection(t,e,n,r)||(this._hasIntersection=!0,!this._includeProper&&this._li.isProper()||(t.addIntersections(this._li,e,0),n.addIntersections(this._li,r,1)),this._li.isProper()&&(this._properIntersectionPoint=this._li.getIntersection(0).copy(),this._hasProper=!0,this._isDoneWhenProperInt&&(this._isDone=!0),this.isBoundaryPoint(this._li,this._bdyNodes)||(this._hasProperInterior=!0))))},fg.prototype.interfaces_=function(){return[]},fg.prototype.getClass=function(){return fg},fg.isAdjacentSegments=function(t,e){return 1===Math.abs(t-e)};var gg=function(t){function e(){t.call(this),this.events=new lc,this.nOverlaps=null}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.prepareEvents=function(){np.sort(this.events);for(var t=0;te||this._maxo?1:0},vg.prototype.interfaces_=function(){return[sl]},vg.prototype.getClass=function(){return vg};var _g=function(t){function e(){t.call(this),this._item=null;var e=arguments[0],n=arguments[1],r=arguments[2];this._min=e,this._max=n,this._item=r}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.query=function(t,e,n){if(!this.intersects(t,e))return null;n.visitItem(this._item)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(dg),mg=function(t){function e(){t.call(this),this._node1=null,this._node2=null;var e=arguments[0],n=arguments[1];this._node1=e,this._node2=n,this.buildExtent(this._node1,this._node2)}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.buildExtent=function(t,e){this._min=Math.min(t._min,e._min),this._max=Math.max(t._max,e._max)},e.prototype.query=function(t,e,n){if(!this.intersects(t,e))return null;null!==this._node1&&this._node1.query(t,e,n),null!==this._node2&&this._node2.query(t,e,n)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e}(dg),xg=function(){this._leaves=new lc,this._root=null,this._level=0};xg.prototype.buildTree=function(){np.sort(this._leaves,new dg.NodeComparator);for(var t=this._leaves,e=null,n=new lc;;){if(this.buildLevel(t,n),1===n.size())return n.get(0);e=t,t=n,n=e}},xg.prototype.insert=function(t,e,n){if(null!==this._root)throw new Error("Index cannot be added to once it has been queried");this._leaves.add(new _g(t,e,n))},xg.prototype.query=function(t,e,n){this.init(),this._root.query(t,e,n)},xg.prototype.buildRoot=function(){if(null!==this._root)return null;this._root=this.buildTree()},xg.prototype.printNode=function(t){Pl.out.println(Fl.toLineString(new ul(t._min,this._level),new ul(t._max,this._level)))},xg.prototype.init=function(){if(null!==this._root)return null;this.buildRoot()},xg.prototype.buildLevel=function(t,e){this._level++,e.clear();for(var n=0;n=2,"found LineString with single point"),this.insertBoundaryPoint(this._argIndex,e[0]),this.insertBoundaryPoint(this._argIndex,e[e.length-1])},e.prototype.getInvalidPoint=function(){return this._invalidPoint},e.prototype.getBoundaryPoints=function(){for(var t=this.getBoundaryNodes(),e=new Array(t.size()).fill(null),n=0,r=t.iterator();r.hasNext();){var i=r.next();e[n++]=i.getCoordinate().copy()}return e},e.prototype.getBoundaryNodes=function(){return null===this._boundaryNodes&&(this._boundaryNodes=this._nodes.getBoundaryNodes(this._argIndex)),this._boundaryNodes},e.prototype.addSelfIntersectionNode=function(t,e,n){if(this.isBoundaryNode(t,e))return null;n===pl.BOUNDARY&&this._useBoundaryDeterminationRule?this.insertBoundaryPoint(t,e):this.insertPoint(t,e,n)},e.prototype.addPolygonRing=function(t,e,n){if(t.isEmpty())return null;var r=pc.removeRepeatedPoints(t.getCoordinates());if(r.length<4)return this._hasTooFewPoints=!0,this._invalidPoint=r[0],null;var i=e,o=n;Xl.isCCW(r)&&(i=n,o=e);var s=new of(r,new Dh(this._argIndex,pl.BOUNDARY,i,o));this._lineEdgeMap.put(t,s),this.insertEdge(s),this.insertPoint(this._argIndex,r[0],pl.BOUNDARY)},e.prototype.insertPoint=function(t,e,n){var r=this._nodes.addNode(e),i=r.getLabel();null===i?r._label=new Dh(t,n):i.setLocation(t,n)},e.prototype.createEdgeSetIntersector=function(){return new gg},e.prototype.addSelfIntersectionNodes=function(t){for(var e=this._edges.iterator();e.hasNext();)for(var n=e.next(),r=n.getLabel().getLocation(t),i=n.eiList.iterator();i.hasNext();){var o=i.next();this.addSelfIntersectionNode(t,o.coord,r)}},e.prototype.add=function(){if(1!==arguments.length)return t.prototype.add.apply(this,arguments);var e=arguments[0];if(e.isEmpty())return null;if(e instanceof rh&&(this._useBoundaryDeterminationRule=!1),e instanceof th)this.addPolygon(e);else if(e instanceof Zc)this.addLineString(e);else if(e instanceof Qc)this.addPoint(e);else if(e instanceof eh)this.addCollection(e);else if(e instanceof Gc)this.addCollection(e);else if(e instanceof rh)this.addCollection(e);else{if(!(e instanceof kc))throw new Error(e.getClass().getName());this.addCollection(e)}},e.prototype.addCollection=function(t){for(var e=0;e50?(null===this._areaPtLocator&&(this._areaPtLocator=new bg(this._parentGeom)),this._areaPtLocator.locate(t)):this._ptLocator.locate(t,this._parentGeom)},e.prototype.findEdge=function(){if(1===arguments.length){var e=arguments[0];return this._lineEdgeMap.get(e)}return t.prototype.findEdge.apply(this,arguments)},e.prototype.interfaces_=function(){return[]},e.prototype.getClass=function(){return e},e.determineBoundary=function(t,e){return t.isInBoundary(e)?pl.BOUNDARY:pl.INTERIOR},e}(Hh),Cg=function(){if(this._li=new jl,this._resultPrecisionModel=null,this._arg=null,1===arguments.length){var t=arguments[0];this.setComputationPrecision(t.getPrecisionModel()),this._arg=new Array(1).fill(null),this._arg[0]=new Sg(0,t)}else if(2===arguments.length){var e=arguments[0],n=arguments[1],r=Ql.OGC_SFS_BOUNDARY_RULE;e.getPrecisionModel().compareTo(n.getPrecisionModel())>=0?this.setComputationPrecision(e.getPrecisionModel()):this.setComputationPrecision(n.getPrecisionModel()),this._arg=new Array(2).fill(null),this._arg[0]=new Sg(0,e,r),this._arg[1]=new Sg(1,n,r)}else if(3===arguments.length){var i=arguments[0],o=arguments[1],s=arguments[2];i.getPrecisionModel().compareTo(o.getPrecisionModel())>=0?this.setComputationPrecision(i.getPrecisionModel()):this.setComputationPrecision(o.getPrecisionModel()),this._arg=new Array(2).fill(null),this._arg[0]=new Sg(0,i,s),this._arg[1]=new Sg(1,o,s)}};Cg.prototype.getArgGeometry=function(t){return this._arg[t].getGeometry()},Cg.prototype.setComputationPrecision=function(t){this._resultPrecisionModel=t,this._li.setPrecisionModel(this._resultPrecisionModel)},Cg.prototype.interfaces_=function(){return[]},Cg.prototype.getClass=function(){return Cg};var Pg=function(){};Pg.prototype.interfaces_=function(){return[]},Pg.prototype.getClass=function(){return Pg},Pg.map=function(){if(arguments[0]instanceof Wl&&gl(arguments[1],Pg.MapOp)){for(var t=arguments[0],e=arguments[1],n=new lc,r=0;r=t.size()?null:t.get(e)},Fg.union=function(t){return new Fg(t).union()},kg.STRTREE_NODE_CAPACITY.get=function(){return 4},Object.defineProperties(Fg,kg);var Gg=function(){};function qg(){return new Bg}function Bg(){this.reset()}Gg.prototype.interfaces_=function(){return[]},Gg.prototype.getClass=function(){return Gg},Gg.union=function(t,e){if(t.isEmpty()||e.isEmpty()){if(t.isEmpty()&&e.isEmpty())return Mg.createEmptyResult(Mg.UNION,t,e,t.getFactory());if(t.isEmpty())return e.copy();if(e.isEmpty())return t.copy()}return t.checkNotGeometryCollection(t),t.checkNotGeometryCollection(e),ug.overlayOp(t,e,Mg.UNION)},Bg.prototype={constructor:Bg,reset:function(){this.s=this.t=0},add:function(t){jg(zg,t,this.t),jg(this,zg.s,this.s),this.s?this.t+=zg.t:this.s=zg.t},valueOf:function(){return this.s}};var zg=new Bg;function jg(t,e,n){var r=t.s=e+n,i=r-e,o=r-i;t.t=e-o+(n-i)}var Ug=1e-6,Vg=Math.PI,Xg=Vg/2,Yg=Vg/4,Hg=2*Vg,Wg=180/Vg,Jg=Vg/180,Zg=Math.abs,Kg=Math.atan,Qg=Math.atan2,$g=Math.cos,td=Math.sin,ed=Math.sqrt;function nd(t){return t>1?0:t<-1?Vg:Math.acos(t)}function rd(t){return t>1?Xg:t<-1?-Xg:Math.asin(t)}function id(){}function od(t,e){t&&ad.hasOwnProperty(t.type)&&ad[t.type](t,e)}var sd={Feature:function(t,e){od(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++rVg?t-Hg:t<-Vg?t+Hg:t,e]}function md(t){return function(e,n){return[(e+=t)>Vg?e-Hg:e<-Vg?e+Hg:e,n]}}function xd(t){var e=md(t);return e.invert=md(-t),e}function Ed(t,e){var n=$g(t),r=td(t),i=$g(e),o=td(e);function s(t,e){var s=$g(e),a=$g(t)*s,u=td(t)*s,l=td(e),c=l*n+a*r;return[Qg(u*i-c*o,a*n-l*r),rd(c*i+u*o)]}return s.invert=function(t,e){var s=$g(e),a=$g(t)*s,u=td(t)*s,l=td(e),c=l*i-u*o;return[Qg(u*i+l*o,a*n+c*r),rd(c*n-a*r)]},s}function bd(t,e){(e=hd(e))[0]-=t,yd(e);var n=nd(-e[1]);return((-e[2]<0?-n:n)+Hg-Ug)%Hg}function wd(){var t,e=[];return{point:function(e,n){t.push([e,n])},lineStart:function(){e.push(t=[])},lineEnd:id,rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))},result:function(){var n=e;return e=[],t=null,n}}}function Id(t,e){return Zg(t[0]-e[0])=0;--o)i.point((c=l[o])[0],c[1]);else r(p.x,p.p.x,-1,i);p=p.p}l=(p=p.o).z,f=!f}while(!p.v);i.lineEnd()}}}function Cd(t){if(e=t.length){for(var e,n,r=0,i=t[0];++re?1:t>=e?0:NaN}_d.invert=_d;var Md,Ld;1===(Md=Pd).length&&(Ld=Md,Md=function(t,e){return Pd(Ld(t),e)});function Od(t){for(var e,n,r,i=t.length,o=-1,s=0;++o=0;)for(e=(r=t[i]).length;--e>=0;)n[--s]=r[e];return n}var Rd=1e9,Td=-Rd;function Ad(t,e,n,r){function i(i,o){return t<=i&&i<=n&&e<=o&&o<=r}function o(i,o,a,l){var c=0,h=0;if(null==i||(c=s(i,a))!==(h=s(o,a))||u(i,o)<0^a>0)do{l.point(0===c||3===c?t:n,c>1?r:e)}while((c=(c+a+4)%4)!==h);else l.point(o[0],o[1])}function s(r,i){return Zg(r[0]-t)0?0:3:Zg(r[0]-n)0?2:1:Zg(r[1]-e)0?1:0:i>0?3:2}function a(t,e){return u(t.x,e.x)}function u(t,e){var n=s(t,1),r=s(e,1);return n!==r?n-r:0===n?e[1]-t[1]:1===n?t[0]-e[0]:2===n?t[1]-e[1]:e[0]-t[0]}return function(s){var u,l,c,h,p,f,g,d,y,v,_,m=s,x=wd(),E={point:b,lineStart:function(){E.point=w,l&&l.push(c=[]);v=!0,y=!1,g=d=NaN},lineEnd:function(){u&&(w(h,p),f&&y&&x.rejoin(),u.push(x.result()));E.point=b,y&&m.lineEnd()},polygonStart:function(){m=x,u=[],l=[],_=!0},polygonEnd:function(){var e=function(){for(var e=0,n=0,i=l.length;nr&&(p-o)*(r-s)>(f-s)*(t-o)&&++e:f<=r&&(p-o)*(r-s)<(f-s)*(t-o)&&--e;return e}(),n=_&&e,i=(u=Od(u)).length;(n||i)&&(s.polygonStart(),n&&(s.lineStart(),o(null,null,1,s),s.lineEnd()),i&&Sd(u,a,e,o,s),s.polygonEnd());m=s,u=l=c=null}};function b(t,e){i(t,e)&&m.point(t,e)}function w(o,s){var a=i(o,s);if(l&&c.push([o,s]),v)h=o,p=s,f=a,v=!1,a&&(m.lineStart(),m.point(o,s));else if(a&&y)m.point(o,s);else{var u=[g=Math.max(Td,Math.min(Rd,g)),d=Math.max(Td,Math.min(Rd,d))],x=[o=Math.max(Td,Math.min(Rd,o)),s=Math.max(Td,Math.min(Rd,s))];!function(t,e,n,r,i,o){var s,a=t[0],u=t[1],l=0,c=1,h=e[0]-a,p=e[1]-u;if(s=n-a,h||!(s>0)){if(s/=h,h<0){if(s0){if(s>c)return;s>l&&(l=s)}if(s=i-a,h||!(s<0)){if(s/=h,h<0){if(s>c)return;s>l&&(l=s)}else if(h>0){if(s0)){if(s/=p,p<0){if(s0){if(s>c)return;s>l&&(l=s)}if(s=o-u,p||!(s<0)){if(s/=p,p<0){if(s>c)return;s>l&&(l=s)}else if(p>0){if(s0&&(t[0]=a+l*h,t[1]=u+l*p),c<1&&(e[0]=a+c*h,e[1]=u+c*p),!0}}}}}(u,x,t,e,n,r)?a&&(m.lineStart(),m.point(o,s),_=!1):(y||(m.lineStart(),m.point(u[0],u[1])),m.point(x[0],x[1]),a||m.lineEnd(),_=!1)}g=o,d=s,y=a}return E}}var Dd=qg();qg();function Fd(t){return t}qg(),qg();var kd=1/0,Gd=kd,qd=-kd,Bd=qd,zd={point:function(t,e){tqd&&(qd=t);eBd&&(Bd=e)},lineStart:id,lineEnd:id,polygonStart:id,polygonEnd:id,result:function(){var t=[[kd,Gd],[qd,Bd]];return qd=Bd=-(Gd=kd=1/0),t}};qg();function jd(t,e,n,r){return function(i,o){var s,a,u,l=e(o),c=i.invert(r[0],r[1]),h=wd(),p=e(h),f=!1,g={point:d,lineStart:v,lineEnd:_,polygonStart:function(){g.point=m,g.lineStart=x,g.lineEnd=E,a=[],s=[]},polygonEnd:function(){g.point=d,g.lineStart=v,g.lineEnd=_,a=Od(a);var t=function(t,e){var n=e[0],r=e[1],i=[td(n),-$g(n),0],o=0,s=0;Dd.reset();for(var a=0,u=t.length;a=0?1:-1,I=w*b,N=I>Vg,S=g*x;if(Dd.add(Qg(S*w*td(I),d*E+S*$g(I))),o+=N?b+w*Hg:b,N^p>=n^_>=n){var C=fd(hd(h),hd(v));yd(C);var P=fd(i,C);yd(P);var M=(N^b>=0?-1:1)*rd(P[2]);(r>M||r===M&&(C[0]||C[1]))&&(s+=N^b>=0?1:-1)}}return(o<-1e-6||o0){for(f||(o.polygonStart(),f=!0),o.lineStart(),t=0;t1&&2&i&&l.push(l.pop().concat(l.shift())),a.push(l.filter(Ud))}return g}}function Ud(t){return t.length>1}function Vd(t,e){return((t=t.x)[0]<0?t[1]-Xg-Ug:Xg-t[1])-((e=e.x)[0]<0?e[1]-Xg-Ug:Xg-e[1])}var Xd=jd((function(){return!0}),(function(t){var e,n=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(o,s){var a=o>0?Vg:-Vg,u=Zg(o-n);Zg(u-Vg)0?Xg:-Xg),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),t.point(o,r),e=0):i!==a&&u>=Vg&&(Zg(n-i)Ug?Kg((td(e)*(o=$g(r))*td(n)-td(r)*(i=$g(e))*td(t))/(i*o*s)):(e+r)/2}(n,r,o,s),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(a,r),e=0),t.point(n=o,r=s),i=a},lineEnd:function(){t.lineEnd(),n=r=NaN},clean:function(){return 2-e}}}),(function(t,e,n,r){var i;if(null==t)i=n*Xg,r.point(-Vg,i),r.point(0,i),r.point(Vg,i),r.point(Vg,0),r.point(Vg,-i),r.point(0,-i),r.point(-Vg,-i),r.point(-Vg,0),r.point(-Vg,i);else if(Zg(t[0]-e[0])>Ug){var o=t[0]0,i=Zg(n)>Ug;function o(t,e){return $g(t)*$g(e)>n}function s(t,e,r){var i=[1,0,0],o=fd(hd(t),hd(e)),s=pd(o,o),a=o[0],u=s-a*a;if(!u)return!r&&t;var l=n*s/u,c=-n*a/u,h=fd(i,o),p=dd(i,l);gd(p,dd(o,c));var f=h,g=pd(p,f),d=pd(f,f),y=g*g-d*(pd(p,p)-1);if(!(y<0)){var v=ed(y),_=dd(f,(-g-v)/d);if(gd(_,p),_=cd(_),!r)return _;var m,x=t[0],E=e[0],b=t[1],w=e[1];E0^_[1]<(Zg(_[0]-x)Vg^(x<=_[0]&&_[0]<=E)){var S=dd(f,(-g+v)/d);return gd(S,p),[_,cd(S)]}}}function a(e,n){var i=r?t:Vg-t,o=0;return e<-i?o|=1:e>i&&(o|=2),n<-i?o|=4:n>i&&(o|=8),o}return jd(o,(function(t){var e,n,u,l,c;return{lineStart:function(){l=u=!1,c=1},point:function(h,p){var f,g=[h,p],d=o(h,p),y=r?d?0:a(h,p):d?a(h+(h<0?Vg:-Vg),p):0;if(!e&&(l=u=d)&&t.lineStart(),d!==u&&(!(f=s(e,g))||Id(e,f)||Id(g,f))&&(g[0]+=Ug,g[1]+=Ug,d=o(g[0],g[1])),d!==u)c=0,d?(t.lineStart(),f=s(g,e),t.point(f[0],f[1])):(f=s(e,g),t.point(f[0],f[1]),t.lineEnd()),e=f;else if(i&&e&&r^d){var v;y&n||!(v=s(g,e,!0))||(c=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1])))}!d||e&&Id(e,g)||t.point(g[0],g[1]),e=g,u=d,n=y},lineEnd:function(){u&&t.lineEnd(),e=null},clean:function(){return c|(l&&u)<<1}}}),(function(n,r,i,o){!function(t,e,n,r,i,o){if(n){var s=$g(e),a=td(e),u=r*n;null==i?(i=e+r*Hg,o=e-u/2):(i=bd(s,i),o=bd(s,o),(r>0?io)&&(i+=r*Hg));for(var l,c=i;r>0?c>o:c4*e&&d--){var x=s+p,E=a+f,b=u+g,w=ed(x*x+E*E+b*b),I=rd(b/=w),N=Zg(Zg(b)-1)e||Zg((v*M+_*L)/m-.5)>.3||s*p+a*f+u*g2?t[2]%360*Jg:0,P()):[y*Wg,v*Wg,_*Wg]},N.precision=function(t){return arguments.length?(I=Kd(C,w=t*t),M()):ed(w)},N.fitExtent=function(t,e){return Jd(N,t,e)},N.fitSize=function(t,e){return function(t,e,n){return Jd(t,[[0,0],e],n)}(N,t,e)},function(){return e=t.apply(this,arguments),N.invert=e.invert&&S,P()}}((function(){return t}))()}var ty=function(t){return function(e,n){var r=$g(e),i=$g(n),o=t(r*i);return[o*i*td(e),o*td(n)]}}((function(t){return(t=nd(t))&&t/td(t)}));function ey(){return $d(ty).scale(79.4188).clipAngle(179.999)}function ny(t,n,r,i){var s=t.properties||{},a="Feature"===t.type?t.geometry:t;if("GeometryCollection"===a.type){var u=[];return q(t,(function(t){var e=ny(t,n,r,i);e&&u.push(e)})),f(u)}var l=function(t){var n=xn(t).geometry.coordinates,r=[-n[0],-n[1]];return ey().rotate(r).scale(e)}(a),c={type:a.type,coordinates:iy(a.coordinates,l)},h=(new Ih).read(c),p=m(x(n,r),"meters"),g=_f.bufferOp(h,p,i);if(!ry((g=(new Nh).write(g)).coordinates))return o({type:g.type,coordinates:oy(g.coordinates,l)},s)}function ry(t){return Array.isArray(t[0])?ry(t[0]):isNaN(t[0])}function iy(t,e){return"object"!=typeof t[0]?e(t):t.map((function(t){return iy(t,e)}))}function oy(t,e){return"object"!=typeof t[0]?e.invert(t):t.map((function(t){return oy(t,e)}))}function sy(t,e,n){void 0===n&&(n={});var r=rt(t),i=rt(e),o=$u.intersection(r.coordinates,i.coordinates);return 0===o.length?null:1===o.length?l(o[0],n.properties):y(o,n.properties)}function ay(t,e,n){void 0===n&&(n={});var r=JSON.stringify(n.properties||{}),i=t[0],o=t[1],s=t[2],a=t[3],u=(o+a)/2,l=(i+s)/2,c=2*e/me([i,u],[s,u],n)*(s-i),h=2*e/me([l,o],[l,a],n)*(a-o),p=c/2,g=2*p,d=Math.sqrt(3)/2*h,y=s-i,v=a-o,_=3/4*g,m=d,x=(y-g)/(g-p/2),E=Math.floor(x),b=(E*_-p/2-y)/2-p/2+_/2,w=Math.floor((v-d)/d),I=(v-w*d)/2,N=w*d-v>d/2;N&&(I-=d/4);for(var S=[],C=[],P=0;P<6;P++){var M=2*Math.PI/6*P;S.push(Math.cos(M)),C.push(Math.sin(M))}for(var L=[],O=0;O<=E;O++)for(var R=0;R<=w;R++){var T=O%2==1;if((0!==R||!T)&&(0!==R||!N)){var A=O*_+i-b,D=R*m+o+I;if(T&&(D-=d/2),!0===n.triangles)ly([A,D],c/2,h/2,JSON.parse(r),S,C).forEach((function(t){n.mask?sy(n.mask,t)&&L.push(t):L.push(t)}));else{var F=uy([A,D],c/2,h/2,JSON.parse(r),S,C);n.mask?sy(n.mask,F)&&L.push(F):L.push(F)}}}return f(L)}function uy(t,e,n,r,i,o){for(var s=[],a=0;a<6;a++){var u=t[0]+e*i[a],c=t[1]+n*o[a];s.push([u,c])}return s.push(s[0].slice()),l([s],r)}function ly(t,e,n,r,i,o){for(var s=[],a=0;a<6;a++){var u=[];u.push(t),u.push([t[0]+e*i[a],t[1]+n*o[a]]),u.push([t[0]+e*i[(a+1)%6],t[1]+n*o[(a+1)%6]]),u.push(t),s.push(l([u],r))}return s}function cy(t){return y(t)}function hy(t){return l(t&&t.geometry.coordinates||[[[180,90],[-180,90],[-180,-90],[180,-90],[180,90]]])}function py(t,e,n){return void 0===n&&(n={}),function(t,e,n,r){void 0===r&&(r={});for(var i=[],o=t[0],s=t[1],a=t[2],u=t[3],c=e/me([o,s],[a,s],r)*(a-o),h=n/me([o,s],[o,u],r)*(u-s),p=a-o,g=u-s,d=Math.floor(p/c),y=Math.floor(g/h),v=(g-y*h)/2,_=o+(p-d*c)/2,m=0;m=i&&o===r.length-1);o++){if(i>=e){var s=e-i;if(s){var u=mn(r[o],r[o-1])-180;return vn(r[o],s,u,n)}return a(r[o])}i+=me(r[o],r[o+1],n)}return a(r[r.length-1])},t.angle=function(t,e,n,r){if(void 0===r&&(r={}),!P(r))throw new Error("options is invalid");if(!t)throw new Error("startPoint is required");if(!e)throw new Error("midPoint is required");if(!n)throw new Error("endPoint is required");var i=t,o=e,s=n,a=b(!0!==r.mercator?mn(i,o):Bi(i,o)),u=b(!0!==r.mercator?mn(s,o):Bi(s,o)),l=Math.abs(a-u);return!0===r.explementary?360-l:l},t.applyFilter=uu,t.area=jr,t.areaFactors=i,t.bbox=Z,t.bboxClip=function(t,e){var n=rt(t),r=n.type,i="Feature"===t.type?t.properties:{},o=n.coordinates;switch(r){case"LineString":case"MultiLineString":var s=[];return"LineString"===r&&(o=[o]),o.forEach((function(t){!function(t,e,n){var r,i,o,s,a,u=t.length,l=Ri(t[0],e),c=[];for(n||(n=[]),r=1;r0)for(var n=0;n0},t.booleanParallel=function(t,e){if(!t)throw new Error("line1 is required");if(!e)throw new Error("line2 is required");if("LineString"!==Us(t,"line1"))throw new Error("line1 must be a LineString");if("LineString"!==Us(e,"line2"))throw new Error("line2 must be a LineString");for(var n=Zn(tn(t)).features,r=Zn(tn(e)).features,i=0;in&&(e.numberOfClusters=n),!0!==e.mutate&&(t=Ie(t));var r=G(t),i=r.slice(0,e.numberOfClusters),o=zs(r,e.numberOfClusters,i),s={};return o.centroids.forEach((function(t,e){s[e]=t})),F(t,(function(t,e){var n=o.idxs[e];t.properties.cluster=n,t.properties.centroid=s[n]})),t},t.collect=function(t,e,n,r){var i=Nt(6),o=e.features.map((function(t){var e;return{minX:t.geometry.coordinates[0],minY:t.geometry.coordinates[1],maxX:t.geometry.coordinates[0],maxY:t.geometry.coordinates[1],property:null===(e=t.properties)||void 0===e?void 0:e[n]}}));return i.load(o),t.features.forEach((function(t){t.properties||(t.properties={});var e=Z(t),n=i.search({minX:e[0],minY:e[1],maxX:e[2],maxY:e[3]}),o=[];n.forEach((function(e){ye([e.minX,e.minY],t)&&o.push(e.property)})),t.properties[r]=o})),t},t.collectionOf=nt,t.combine=function(t){var e={MultiPoint:{coordinates:[],properties:[]},MultiLineString:{coordinates:[],properties:[]},MultiPolygon:{coordinates:[],properties:[]}};return F(t,(function(t){var n,r,i,o;switch(null===(o=t.geometry)||void 0===o?void 0:o.type){case"Point":e.MultiPoint.coordinates.push(t.geometry.coordinates),e.MultiPoint.properties.push(t.properties);break;case"MultiPoint":(n=e.MultiPoint.coordinates).push.apply(n,t.geometry.coordinates),e.MultiPoint.properties.push(t.properties);break;case"LineString":e.MultiLineString.coordinates.push(t.geometry.coordinates),e.MultiLineString.properties.push(t.properties);break;case"MultiLineString":(r=e.MultiLineString.coordinates).push.apply(r,t.geometry.coordinates),e.MultiLineString.properties.push(t.properties);break;case"Polygon":e.MultiPolygon.coordinates.push(t.geometry.coordinates),e.MultiPolygon.properties.push(t.properties);break;case"MultiPolygon":(i=e.MultiPolygon.coordinates).push.apply(i,t.geometry.coordinates),e.MultiPolygon.properties.push(t.properties)}})),f(Object.keys(e).filter((function(t){return e[t].coordinates.length})).sort().map((function(t){return o({type:t,coordinates:e[t].coordinates},{collectedProperties:e[t].properties})})))},t.concave=function(t,e){void 0===e&&(e={});var n=e.maxEdge||1/0,r=xe(function(t){var e=[],n={};return F(t,(function(t){if(t.geometry){var r=t.geometry.coordinates.join("-");Object.prototype.hasOwnProperty.call(n,r)||(e.push(t),n[r]=!0)}})),f(e)}(t));if(r.features=r.features.filter((function(t){var r=t.geometry.coordinates[0][0],i=t.geometry.coordinates[0][1],o=t.geometry.coordinates[0][2],s=me(r,i,e),a=me(i,o,e),u=me(r,o,e);return s<=n&&a<=n&&u<=n})),r.features.length<1)return null;var i=$e(r);return 1===i.coordinates.length&&(i.coordinates=i.coordinates[0],i.type="Polygon"),o(i)},t.containsNumber=$,t.convertArea=S,t.convertDistance=N,t.convertLength=N,t.convex=de,t.coordAll=G,t.coordEach=R,t.coordReduce=T,t.createBins=au,t.degrees2radians=I,t.degreesToRadians=I,t.destination=vn,t.difference=function(t,e){var n=rt(t),r=rt(e),i=t.properties||{},o=$u.difference(n.coordinates,r.coordinates);return 0===o.length?null:1===o.length?l(o[0],i):y(o,i)},t.dissolve=function(t,e){if(!P(e=e||{}))throw new Error("options is invalid");var n=e.propertyName;nt(t,"Polygon","dissolve");var r=[];if(!e.propertyName)return ni(y($u.union.apply(null,t.features.map((function(t){return t.geometry.coordinates})))));var i={};F(t,(function(t){Object.prototype.hasOwnProperty.call(i,t.properties[n])||(i[t.properties[n]]=[]),i[t.properties[n]].push(t)}));for(var o=Object.keys(i),s=0;s0&&(s=l(o).geometry),qa(s,a),o=a.slice(0);n.push(l(o,i));break;case"MultiPolygon":o=[[[]]];for(var c=0;c0&&(s=y(o).geometry),Ba(s,a),o=a.slice(0);n.push(y(o,i));break;default:throw new Error("geometry is invalid, must be Polygon or MultiPolygon")}})),f(n)},t.polygonTangents=function(t,e){var n,r,i,o,s=Q(t),u=Q(e),l=Z(e),c=0,h=null;switch(s[0]>l[0]&&s[0]l[1]&&s[1] is required");if("boolean"!=typeof n)throw new Error(" must be a boolean");if("boolean"!=typeof r)throw new Error(" must be a boolean");!1===r&&(t=Ie(t));var i=[];switch(t.type){case"GeometryCollection":return q(t,(function(t){Hi(t,n)})),t;case"FeatureCollection":return F(t,(function(t){F(Hi(t,n),(function(t){i.push(t)}))})),f(i)}return Hi(t,n)},t.rhumbBearing=Bi,t.rhumbDestination=ji,t.rhumbDistance=Ar,t.round=_,t.sample=function(t,e){if(!t)throw new Error("featurecollection is required");if(null==e)throw new Error("num is required");if("number"!=typeof e)throw new Error("num must be a number");return f(function(t,e){var n,r,i=t.slice(0),o=t.length,s=o-e;for(;o-- >s;)n=i[r=Math.floor((o+1)*Math.random())],i[r]=i[o],i[o]=n;return i.slice(s)}(t.features,e))},t.sector=function(t,e,n,r,i){if(!P(i=i||{}))throw new Error("options is invalid");var o=i.properties;if(!t)throw new Error("center is required");if(null==n)throw new Error("bearing1 is required");if(null==r)throw new Error("bearing2 is required");if(!e)throw new Error("radius is required");if("object"!=typeof i)throw new Error("options must be an object");if(qi(n)===qi(r))return _n(t,e,i);var s=Q(t),a=bi(t,e,n,r,i),u=[[s]];return R(a,(function(t){u[0].push(t)})),u[0].push(s),l(u,o)},t.segmentEach=U,t.segmentReduce=V,t.shortestPath=function(t,e,n){if(!P(n=n||{}))throw new Error("options is invalid");var r=n.resolution,i=n.minDistance,s=n.obstacles||f([]);if(!t)throw new Error("start is required");if(!e)throw new Error("end is required");if(r&&!C(r)||r<=0)throw new Error("options.resolution must be a number, greater than 0");if(i)throw new Error("options.minDistance is not yet implemented");var u=K(t),l=K(e);switch(t=a(u),e=a(l),it(s)){case"FeatureCollection":if(0===s.features.length)return h([u,l]);break;case"Polygon":s=f([o(rt(s))]);break;default:throw new Error("invalid obstacles")}var c=s;c.features.push(t),c.features.push(e);var p=Z(ts(gn(Z(c)),1.15));r||(r=me([p[0],p[1]],[p[2],p[1]],n)/100),c.features.pop(),c.features.pop();for(var g=p[0],d=p[1],y=p[2],v=p[3],_=r/me([g,d],[y,d],n)*(y-g),m=r/me([g,d],[g,v],n)*(v-d),x=y-g,E=v-d,b=Math.floor(x/_),w=Math.floor(E/m),I=(x-b*_)/2,N=[],S=[],M=[],L=[],O=1/0,R=1/0,T=v-(E-w*m)/2,A=0;T>=d;){for(var D=[],F=[],k=g+I,G=0;k<=y;){var q=a([k,T]),B=Js(q,s);D.push(B?0:1),F.push(k+"|"+T);var z=me(q,t);!B&&z + + + + @@ -322,6 +326,10 @@ + + + + @@ -414,6 +422,8 @@
+ +
diff --git a/components/ml-assistant/ml-assistant.js b/components/ml-assistant/ml-assistant.js index feb04fc49..5f9808a72 100644 --- a/components/ml-assistant/ml-assistant.js +++ b/components/ml-assistant/ml-assistant.js @@ -177,6 +177,9 @@ Assistant.prototype.__assignEventListener = function() { iElt.checked = false; }); event.target.checked = true; + const modelKey = this.modelList.querySelector(`#${elt.getAttribute('for')}`).value; + console.log('model key: ', modelKey); + this.__selectModel(modelKey); }) }) @@ -297,7 +300,7 @@ Assistant.prototype.__enableAssistant = function() { // Handle model selection Assistant.prototype.__selectModel = function(modelValue) { // Change UI - this._viewer.raiseEvent('select-model', {model: modelValue}); + mltools.loadModel(modelValue); } // Handle open model info modal diff --git a/components/ml-assistant/ml-tool.js b/components/ml-assistant/ml-tool.js new file mode 100644 index 000000000..5768fdd50 --- /dev/null +++ b/components/ml-assistant/ml-tool.js @@ -0,0 +1,519 @@ +const IDB_URL = 'indexeddb://'; + +class mlTools { + constructor() { + this.init(); + } + + init() { + console.log('run init'); + this.canvas; + this.context; + this.data = new Map(); + this.threshold = this.t = 120; + this.radius = 30; + this.mode = 0; + this.model = 0; + this.modelLoaded = false; + this.size = 0; + this.ch = 4; + this.undo; + this.temp = document.createElement('canvas'); + this.fullPredict = document.createElement('canvas'); + } + + initcanvas(canvas) { + this.canvas = canvas; + this.context = canvas.getContext('2d'); + } + + /** + * + * @param {*} x1 + * @param {*} y1 + * @param {*} w + * @param {*} h + * @param {*} th + * @returns + */ + detectContours(x1, y1, w, h, th, context = this.context, invert = true) { + const imgCanvasData = context.getImageData(x1, y1, w, h); + let img = cv.matFromImageData(imgCanvasData); + + // Convert the image to grayscale + let gray = new cv.Mat(); + cv.cvtColor(img, gray, cv.COLOR_RGBA2GRAY); + + let thresholdImg1 = new cv.Mat(); + cv.threshold(gray, thresholdImg1, th, 255, cv.THRESH_BINARY) + this.showmatImg(thresholdImg1, document.querySelector('#edge-img')); + let contours1 = new cv.MatVector(); + let hierarchy1 = new cv.Mat(); + cv.findContours(thresholdImg1, contours1, hierarchy1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + + if (invert) { + let thresholdImg2 = new cv.Mat(); + cv.threshold(gray, thresholdImg2, th, 255, cv.THRESH_BINARY_INV) + let contours2 = new cv.MatVector(); + let hierarchy2 = new cv.Mat(); + cv.findContours(thresholdImg2, contours2, hierarchy2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + return [contours1, contours2] + } + + return [contours1]; + } + + /** + * Compute the overlap area between contour and polygon + * @param contour {number[][]} openCV contour data + * @param polygon {number[][]} polygon data + * @return {number} overlap area + */ + overlapArea(contour, polygon) { + const contour2 = contour.slice(); + const polygon2 = polygon.slice(); + contour2.push(contour2[0]); + polygon2.push(polygon2[0]); + const contourTurf = turf.polygon([contour2]); + const polygonTurf = turf.polygon([polygon2]); + const intersection = turf.intersect(contourTurf, polygonTurf); + + if (!intersection) { + return 0.0; + } + + return turf.area(intersection); + } + + /** + * Convert contour data into array + * @param contour {any} openCV contour data + * @return {number[][]} contour data array + */ + convertToArray(contour) { + const contourData = contour.data32S; + let contourPoints = []; + for (let j = 0; j maxArea) { + maxArea = area; + fitContour = contour; + } + } + } + + return fitContour; + } + + /** + * Determine whether one point close to its boundary + * @param {number[][]} contour + * @param {any} expansionBound + * @param {number} epsilon + * @returns {boolean} + */ + closeBoundary(contour, expansionBound, epsilon) { + let close = false; + for (let i = 0; i < contour.length; i++) { + if (contour[i][0] <= epsilon + || contour[i][0] >= expansionBound.w - epsilon + || contour[i][1] <= epsilon + || contour[i][1] >= expansionBound.h - epsilon) { + close = true; + break; + } + } + return close; + } + + /** + * Get coordinate parameter of polygon boundary + * @param {number[][]} polygon + * @return {any} {x: left, y: top, w: width, h: height} + */ + getCoordinate(polygon) { + let x1 = polygon[0][0]; + let y1 = polygon[0][1]; + let x2 = polygon[0][0]; + let y2 = polygon[0][1]; + + for (let i = 0; i < polygon.length; i++) { + if (x1 > polygon[i][0]) { + x1 = polygon[i][0]; + } + if (y1 > polygon[i][1]) { + y1 = polygon[i][1]; + } + if (x2 < polygon[i][0]) { + x2 = polygon[i][0]; + } + if (y2 < polygon[i][1]) { + y2 = polygon[i][1]; + } + } + return { + x: x1, + y: y1, + w: x2 - x1, + h: y2 - y1, + } + } + + /** + * Return boundary information with expansion value + * @param {any} originalBound + * @param {number} expansionValue + */ + getExpansionCoordicate(originalBound, expansionValue) { + return { + x: ~~(originalBound.x - originalBound.w * expansionValue/(100 * 2)), + y: ~~(originalBound.y - originalBound.h * expansionValue/(100 * 2)), + w: ~~(originalBound.w * (1 + expansionValue/100)), + h: ~~(originalBound.h * (1 + expansionValue/100)), + } + } + + /** + * Realign position of polygon like array + * @param {number[][]} array - input array + * @param {number} x - left position of new coordinate + * @param {number} y - top position of new coordinate + * @return {number[][]} processed array + */ + reAlign(array, x, y) { + if (array === undefined) { + return []; + } + for (let i = 0; i < array.length; i++) { + array[i][0] -= x; + array[i][1] -= y; + } + return array; + } + + /** + * Process drawing polygon without using any model + * @param polygon {number[][]} drawing polygon data + * @param threshold {number} threshold for edge detection + * @param expansion {number} expansion percentage from existing data + * @return {number[][]} processed polygon + */ + applyDrawNoModel(polygon, threshold, expansion) { + // remove last point from the polygon + polygon.pop(); + + // get current polygon coordinate (left, top, width, height) + const polygonBound = this.getCoordinate(polygon); + + // get expansion coordinate (left, top, width, height) + const expansionBound = this.getExpansionCoordicate(polygonBound, expansion); + + // get contours from detect edges image + const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, expansionBound.h, threshold); + + // re-align polygon origin + polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); + + // get most fit contour + let fitContour = this.mostFitContour(contours, polygon, expansionBound); + console.log('fitContour: ', fitContour); + + // re-align the most fit contour + fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + + if (fitContour.length === 0) { + return []; + } + // add last point into the most fit contour + fitContour.push(fitContour[0]); + + return fitContour; + } + + /** + * Load model when annotation have been enable + * @param {string} key - model key + * @return {Promise} + */ + loadModel(key) { + return new Promise((resolve, reject) => { + try { + if (this.model) { + this.model.dispose(); + } + const tx = db.transaction('models_store', 'readonly'); + const store = tx.objectStore('models_store'); + const req = store.get(key); + + req.onsuccess = async function(e) { + // self.showProgress('Loading model...'); + + // Keras sorts the labels by alphabetical order. + const inputShape = e.target.result.input_shape; + console.log('inputShape: ', inputShape); + this.size = parseInt(inputShape[1]); + this.ch = parseInt(inputShape[3]); + + this.model = await tf.loadLayersModel(IDB_URL + key); + console.log('Model Loaded'); + const memory = tf.memory(); + console.log('Model Memory Usage'); + console.log('GPU : ' + memory.numBytesInGPU + ' bytes'); + console.log('Total : ' + memory.numBytes + ' bytes'); + + // tfvis.show.modelSummary({name: 'Model Summary', tab: 'Model Inspection'}, model); + tf.tidy(()=>{ + // Warmup the model before using real data. + this.model.predict(tf.zeros([1, this.size, this.size, this.ch])); + // self.showProgress('Model loaded...'); + resolve(true) + }); + }.bind(this) + } catch (error) { + console.log('fail to load model: ', error); + reject(false); + } + }) + } + + /** + * Make + * @param {any} img tensorflow data + * @param {number} ch - number of channel process by model (gray: 1, rgb: 4) + * @return {any} - process image data + */ + channelProcessing(img, ch=1) { + if (ch == 1) { + return tf.image.resizeBilinear(img, [imageSize, imageSize]).mean(2); + } else { + return tf.image.resizeBilinear(img, [imageSize, imageSize]); + } + } + + /** + * Scaling processing for model input images + * @param {any} img - image tensorflow data + * @param {string} scaleMethod - model scaling method + * @return {any} - scaled image data + */ + pixelScaling(img, scaleMethod) { + if (scaleMethod == 'no_scale') { + return img + } else if (scaleMethod == 'norm') { + // Pixel Normalization: scale pixel values to the range 0-1. + const scale = tf.scalar(255); + return img.div(scale); + } else if (scaleMethod == 'center') { + // Pixel Centering: scale pixel values to have a zero mean. + const mean = img.mean(); + return img.sub(mean); + } else { + // Pixel Standardization: scale pixel values to have a zero mean and unit variance. + const mean = img.mean(); + const std = (img.squaredDifference(mean).sum()).div(img.flatten().shape).sqrt(); + return img.sub(mean).div(std); + } + } + + /** + * Get expansion coordinate parameter coresponding with using model + * @param {number} step - current model input size px + * @param {any} polygonBound - current polygon boundary parameter + * @param {number} expansionValue - choosen expansion value + * @return {any} expansion boundary parameter + */ + getModelExpansionCoordicate(step, polygonBound, expansionValue) { + const extendX = Math.ceil(polygonBound.w * (1 + expansionValue / 100) / step) * step - polygonBound.w; + const extendY = Math.ceil(polygonBound.h * (1 + expansionValue / 100) / step) * step - polygonBound.h; + return { + x: polygonBound.x - ~~(extendX/2), + y: polygonBound.y - ~~(extendY/2), + w: polygonBound.w + extendX, + h: polygonBound.h + extendY, + } + } + + /** + * Get list of coordinates + * @param {number} step - model input size + * @param {any} expansionBound - expansion boundary parameter + * @return {any[]} - list of grid pieces coordinates + */ + getGridCoordinate(step, expansionBound) { + const numStepX = ~~(expansionBound.w / step); + const numStepY = ~~(expansionBound.h / step); + let gridBounds = []; + for (let i = 0; i < numStepX; i++) { + for (let j = 0; j < numStepY; j++) { + gridBounds.push({ + x: expansionBound.x + i * step, + y: expansionBound.y + j * step, + w: step, + h: step, + }) + } + } + return gridBounds; + } + + /** + * Using imported model for autocorrect user draw polygon + * @param {any} model - processing model + * @param {number} size - model input image size px + * @param {number} ch - model input channel + * @param {string} scaleMethod - model scale method + * @param {number[][]} polygon - user draw polygon data (already align) + * @param {number} threshold - upper threshold value for canny detection + * @param {number} expansion - expansion percentage + */ + async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion) { + console.log('applyDrawModel'); + // remove last point from the polygon + polygon.pop(); + + // get current polygon coordinate (left, top, width, height) + const polygonBound = this.getCoordinate(polygon); + + // get expansion coordinate (left, top, width, height) + const expansionBound = this.getModelExpansionCoordicate(size, polygonBound, expansion); + + // get grid coordinate with grid size is model size + const gridBounds = this.getGridCoordinate(size, expansionBound); + console.log('gridBounds: ', gridBounds); + + // loop over all pieces of image and run the model + this.fullPredict.getContext('2d').clearRect(0, 0, this.fullPredict.width, this.fullPredict.height); + this.fullPredict.width = expansionBound.w; + this.fullPredict.height = expansionBound.h; + for (let i = 0; i < gridBounds.length; i++) { + // get image data + const imgCanvasData = this.context.getImageData(gridBounds[i].x, gridBounds[i].y, size, size); + console.log('imgCanvasData: ', imgCanvasData); + let val; + tf.tidy(() => { + const img = tf.browser.fromPixels(imgCanvasData).toFloat(); + console.log('img: ', img); + // const channedProcessedImg = this.channelProcessing(img, ch); + // console.log('channedProcessedImg: ', channedProcessedImg); + // const pixelScaledImg = this.pixelScaling(channedProcessedImg, scaleMethod); + // console.log('pixelScaledImg: ', pixelScaledImg); + let img2; + if (ch == 1) { + img2 = tf.image.resizeBilinear(img, [size, size]).mean(2); + } else { + img2 = tf.image.resizeBilinear(img, [size, size]); + } + console.log('img2: ', img2); + const batched = img2.reshape([1, size, size, ch]); + console.log('batched: ', batched); + let values = model.predict(batched).dataSync(); + console.log('values: ', values); + values = Array.from(values); + // scale values + values = values.map((x) => x * 255); + val = []; + while (values.length > 0) val.push(values.splice(0, size)); + }) + tf.engine().startScope(); + await tf.browser.toPixels(val, this.temp); + this.fullPredict.getContext('2d').drawImage(this.temp, gridBounds[i].x - expansionBound.x, gridBounds[i].y - expansionBound.y); + tf.engine().endScope(); + } + + this.showCanvas(this.fullPredict, document.querySelector('#edge-img')); + + const fullPredictCanvas = this.fullPredict.getContext('2d'); + + // get contours from detect edges image + const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, threshold, fullPredictCanvas, false); + + // re-align polygon origin + polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); + + // get most fit contour + let fitContour = this.mostFitContour(contours, polygon, expansionBound); + + // re-align the most fit contour + fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + + if (fitContour.length === 0) { + return []; + } + // add last point into the most fit contour + fitContour.push(fitContour[0]); + + return fitContour; + } + + async applyDraw(polygon, threshold, expansion, scaleMethod = 'no_scale') { + console.log('model: ', this.model); + if (this.model && this.model !== 'watershed') { + return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, expansion); + } else { + return this.applyDrawNoModel(polygon, threshold, expansion) + } + } + + showmatImg(edges, elt) { + // Create a new canvas for displaying the edges + empty(elt) + var edgesCanvas = document.createElement('canvas'); + edgesCanvas.width = edges.cols; + edgesCanvas.height = edges.rows; + var edgesCtx = edgesCanvas.getContext('2d'); + let data = [] + for (let i = 0; i < edges.data.length; i++) { + data.push(edges.data[i]); + data.push(edges.data[i]); + data.push(edges.data[i]); + data.push(225); + } + + // Convert the edges data to an image + var edgesData = new ImageData( + new Uint8ClampedArray(data), + edges.cols, + edges.rows + ); + + // Draw the edges on the canvas + edgesCtx.putImageData(edgesData, 0, 0); + + // Append the canvas to the document body or any other container + elt.appendChild(edgesCanvas); + } + + showCanvas(canvas, elt) { + empty(elt); + elt.appendChild(canvas); + } +} + +var mltools = new mlTools(); diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index cb745fe05..83ea8dbaa 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -457,6 +457,8 @@ this.align_fy = this._viewer.drawer.canvas.width/this._display_.width; this.align_fx = this._viewer.drawer.canvas.height/this._display_.height; + // ml tools + mltools.initcanvas(this._viewer.drawer.canvas); if ( 0 > img_point.x || this.imgWidth < img_point.x || @@ -1035,7 +1037,7 @@ * @private * __endNewFeature create a new feature data. */ - __endNewFeature: function() { + __endNewFeature: async function() { if (this.drawMode == 'point') { this._current_path_.properties.style.color = this.style.color; this._current_path_.properties.style.lineJoin = this.style.lineJoin; @@ -1098,6 +1100,9 @@ // align points = this.__align(points); + const mlPoints = await this.__mlDraw(points); + points = mlPoints; + // simplify and postprocess if(this.isMoving) spen.smoothness = spen.s; if (!(this.drawMode === 'grid') && this._simplify) @@ -1392,6 +1397,30 @@ this._current_path_.geometry.coordinates[0] = data; this.__endNewFeature(); this.drawMode = t; + }, + + /** + * Align functions + *@param {points} + *@return {points} + */ + __mlDraw: async function(points) { + var dist = new Array(); + var ol = points; + for (i = 0; i < ol.length; i++) { + dist.push(new OpenSeadragon.Point(ol[i][0], ol[i][1])); + dist[i] = this._viewer.viewport.imageToWindowCoordinates(dist[i]); + dist[i] = [Math.floor(dist[i].x * this.align_fx), Math.floor(dist[i].y * this.align_fy)]; + } + dist = await mltools.applyDraw(dist, 70, 30); + for (i = 0; i < dist.length; i++) { + dist[i] = new OpenSeadragon.Point(dist[i][0] / this.align_fx, dist[i][1] / this.align_fy); + dist[i] = this._viewer.viewport.windowToImageCoordinates(dist[i]); + dist[i].x = Math.floor(dist[i].x); + dist[i].y = Math.floor(dist[i].y); + points[i] = [dist[i].x, dist[i].y]; + } + return points; } }; From ca997d01783aeca13c55273111beee6356f1eb03 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Wed, 12 Jul 2023 19:22:16 +0700 Subject: [PATCH 10/46] Update machine learning draw assistant --- apps/viewer/init.js | 10 +- components/ml-assistant/ml-assistant.css | 27 +++ components/ml-assistant/ml-assistant.js | 99 +++++++--- components/ml-assistant/ml-tool.js | 177 ++++++++++++++---- .../openseadragon-canvas-draw-overlay.js | 42 +++-- 5 files changed, 272 insertions(+), 83 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index cfe251ed9..cecc331e0 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -855,7 +855,7 @@ async function initUIcomponents() { width: 240, contentPadding: 5, position: 'right', - height: '30vh', + height: '71vh', top: '30px' }); @@ -1036,6 +1036,14 @@ async function initUIcomponents() { showInfo(); }) + $CAMIC.viewer.addHandler('ml-draw-setting-change', () => { + if (!$CAMIC.viewer.canvasDrawInstance) return; + const canvasDraw = $CAMIC.viewer.canvasDrawInstance; + + if (canvasDraw._draws_data_.length) { + canvasDraw.__endNewFeature(true); + } + }) } // Shows the uploaded models' details diff --git a/components/ml-assistant/ml-assistant.css b/components/ml-assistant/ml-assistant.css index 99252b070..4064db563 100644 --- a/components/ml-assistant/ml-assistant.css +++ b/components/ml-assistant/ml-assistant.css @@ -401,3 +401,30 @@ thead, tfoot { font-size: 14px; cursor: pointer; } + +.processed-image, .model-predict-image { + /* display: none; */ + width: 100%; + height: 150px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.processed-image-container, .model-predict-image-container { + width: 90%; + height: 70%; + margin: 10px; + border: 1px solid rgb(150, 142, 255); + border-radius: 6px; + justify-content: center; + display: flex; +} + +.img-label { + width: 95%; + height: 15px; + font-size: 12px; + font-weight: 700; +} \ No newline at end of file diff --git a/components/ml-assistant/ml-assistant.js b/components/ml-assistant/ml-assistant.js index 5f9808a72..c71ad69ea 100644 --- a/components/ml-assistant/ml-assistant.js +++ b/components/ml-assistant/ml-assistant.js @@ -56,6 +56,8 @@ Assistant.prototype.__clearUI = function() { this.annotateModeZone = null; this.settingZone = null; + this.processedImgContainer = null; + this.modelPredictImgContainer = null; }; Assistant.prototype.__refreshUI = async function() { @@ -79,7 +81,7 @@ Assistant.prototype.__refreshUI = async function() {
  • - +
  • @@ -115,26 +117,44 @@ Assistant.prototype.__refreshUI = async function() {
- +
Radius
- + 30
Threshold
- + 90
Roughness
- + 4
+
+
Kernel Size
+ + 0 +
+
+
Iteration
+ + 1 +
+
+
+ +
+
+
+ +
`; @@ -153,7 +173,11 @@ Assistant.prototype.__refreshUI = async function() { radius: this.view.querySelector('.radius-setting'), threshold: this.view.querySelector('.threshold-setting'), roughness: this.view.querySelector('.roughness-setting'), + kernel_size: this.view.querySelector('.separation-kernel-size-setting'), + iteration: this.view.querySelector('.separation-iteration-setting'), } + this.processedImgContainer = this.view.querySelector('.processed-image-container'), + this.modelPredictImgContainer = this.view.querySelector('.model-predict-image-container'), await this.__createModelList(); @@ -178,7 +202,6 @@ Assistant.prototype.__assignEventListener = function() { }); event.target.checked = true; const modelKey = this.modelList.querySelector(`#${elt.getAttribute('for')}`).value; - console.log('model key: ', modelKey); this.__selectModel(modelKey); }) }) @@ -214,18 +237,45 @@ Assistant.prototype.__assignEventListener = function() { this.settingZone.radius.querySelector('input').addEventListener('change', (event) => { this.settingZone.radius.querySelector('span').textContent = event.target.value; // TODO process ROI processing + if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } }) // Change threshold event this.settingZone.threshold.querySelector('input').addEventListener('change', (event) => { this.settingZone.threshold.querySelector('span').textContent = event.target.value; // TODO process ROI processing + if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } }) // Change roughness event this.settingZone.roughness.querySelector('input').addEventListener('change', (event) => { this.settingZone.roughness.querySelector('span').textContent = event.target.value; // TODO process ROI processing + if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } + }) + + // Change separation kernel size event + this.settingZone.kernel_size.querySelector('input').addEventListener('change', (event) => { + this.settingZone.kernel_size.querySelector('span').textContent = event.target.value; + // TODO process ROI processing + if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } + }) + + // Change separation iteration event + this.settingZone.iteration.querySelector('input').addEventListener('change', (event) => { + this.settingZone.iteration.querySelector('span').textContent = event.target.value; + // TODO process ROI processing + if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } }) } @@ -249,8 +299,6 @@ Assistant.prototype.__createModelList = async function() { dict.title = title; dict.value = value; dict.checked = false; - // Saving to previously defined model names - // modelName.push(dict['title']); dropDownList.push(dict); } }); @@ -291,15 +339,12 @@ Assistant.prototype.__updateModelList= function() { this.__createModelList(); } -// Handle enable machine learning assistant -Assistant.prototype.__enableAssistant = function() { - // Change UI - this._viewer.raiseEvent('enable-assistant', {}); +Assistant.prototype.__isEnableAssistant = function() { + return this.enableBtn.checked; } // Handle model selection Assistant.prototype.__selectModel = function(modelValue) { - // Change UI mltools.loadModel(modelValue); } @@ -308,40 +353,34 @@ Assistant.prototype.__openModelInfo = function() { this._viewer.raiseEvent('open-model-info', {}); } +// TODO // Handle model delete Assistant.prototype.__deleteModel = function(modelValue) { - // Delete model from resource - // Remove UI this.__updateModelList(); } -// Handle select mode -Assistant.prototype.__selectMode = function(mode) { - // Change UI -} - // Get mode Assistant.prototype.__getAssistantMode = function() { - const mode = {}; - return mode; -} - -// Handle setting mode change -Assistant.prototype.__settingModeChangeHandler = function() { - // Change UI + return this.annotateModeZone.querySelector('input[checked]').value; } // Get setting mode value Assistant.prototype.__getSettingModes = function() { const settingMode = { - radius: this.settingZone.radius.value, - threshold: this.settingZone.threshold.value, - roughness: this.settingZone.threshold.value, + radius: parseFloat(this.settingZone.radius.querySelector('input').value), + threshold: parseFloat(this.settingZone.threshold.querySelector('input').value), + roughness: parseFloat(this.settingZone.roughness.querySelector('input').value), + kernel_size: parseFloat(this.settingZone.kernel_size.querySelector('input').value), + iteration: parseFloat(this.settingZone.iteration.querySelector('input').value), } return settingMode; } +Assistant.prototype.__getScaleMethod = function() { + return this.pixelScaleList.querySelector('input[checked]').value; +} + Assistant.prototype.__createElementFromHTML= function(htmlString) { var div = document.createElement('div'); div.innerHTML = htmlString.trim(); diff --git a/components/ml-assistant/ml-tool.js b/components/ml-assistant/ml-tool.js index 5768fdd50..246e59164 100644 --- a/components/ml-assistant/ml-tool.js +++ b/components/ml-assistant/ml-tool.js @@ -36,7 +36,7 @@ class mlTools { * @param {*} th * @returns */ - detectContours(x1, y1, w, h, th, context = this.context, invert = true) { + detectContours(x1, y1, w, h, th, size, iter, context = this.context, invert = true) { const imgCanvasData = context.getImageData(x1, y1, w, h); let img = cv.matFromImageData(imgCanvasData); @@ -46,23 +46,40 @@ class mlTools { let thresholdImg1 = new cv.Mat(); cv.threshold(gray, thresholdImg1, th, 255, cv.THRESH_BINARY) - this.showmatImg(thresholdImg1, document.querySelector('#edge-img')); + const sureFgImg1 = this.thresholdImgToForegroundImg(thresholdImg1, size, iter, 2); + this.showmatImg(sureFgImg1, document.querySelector('.processed-image-container')); let contours1 = new cv.MatVector(); let hierarchy1 = new cv.Mat(); - cv.findContours(thresholdImg1, contours1, hierarchy1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + cv.findContours(sureFgImg1, contours1, hierarchy1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); if (invert) { let thresholdImg2 = new cv.Mat(); cv.threshold(gray, thresholdImg2, th, 255, cv.THRESH_BINARY_INV) + const sureFgImg2 = this.thresholdImgToForegroundImg(thresholdImg2, size, iter, 2); let contours2 = new cv.MatVector(); let hierarchy2 = new cv.Mat(); - cv.findContours(thresholdImg2, contours2, hierarchy2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + cv.findContours(sureFgImg2, contours2, hierarchy2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); return [contours1, contours2] } return [contours1]; } + thresholdImgToForegroundImg(thresholdImg, erode_size = 2, iteration = 1, kernel_size = 3) { + // Perform morphological operations to enhance separation + const kernel = new cv.Mat(); + cv.Mat.ones(kernel_size, kernel_size, cv.CV_8U).copyTo(kernel); + const opening = new cv.Mat(); + cv.morphologyEx(thresholdImg, opening, cv.MORPH_OPEN, kernel); + const morph = new cv.Mat(); + cv.morphologyEx(opening, morph, cv.MORPH_CLOSE, kernel); + const erode = new cv.Mat(); + const erode_kernel = new cv.Mat(); + cv.Mat.ones(erode_size, erode_size, cv.CV_8U).copyTo(erode_kernel); + cv.erode(morph, erode, erode_kernel, new cv.Point(-1,-1), iteration) + return erode; + } + /** * Compute the overlap area between contour and polygon * @param contour {number[][]} openCV contour data @@ -107,21 +124,22 @@ class mlTools { * @param h {number} height of orignal image * @return {number[][]} the most fit contour data array */ - mostFitContour(contours, polygon, expansionBound) { + mostFitContour(contours, polygon, expansionBound, erode_size = 2, iteration = 1) { let maxArea = 0; let area; let fitContour; + let expandedContour; for (let j = 0; j < contours.length; j++) { for (let i = 0; i < contours[j].size(); i++) { let contour = contours[j].get(i); if (cv.contourArea(contour, false) < 1) { continue; } - contour = this.convertToArray(contour); - if (this.closeBoundary(contour, expansionBound, 3)) { + const contourArray = this.convertToArray(contour); + if (this.closeBoundary(contourArray, expansionBound, 3)) { continue; } - area = this.overlapArea(contour, polygon); + area = this.overlapArea(contourArray, polygon); if (area > maxArea) { maxArea = area; fitContour = contour; @@ -129,7 +147,41 @@ class mlTools { } } - return fitContour; + if (fitContour) { + expandedContour = this.expandContour(fitContour, expansionBound.w, expansionBound.h, erode_size, iteration); + } + if (!expandedContour) { + return []; + } + const fit_contour = this.convertToArray(expandedContour); + return fit_contour; + } + + expandContour(contour, width, height, size, iter) { + const mask = new cv.Mat.zeros(height, width, cv.CV_8UC1); + for (let i = 0; i < width; i++) { + for (let j = 0; j < height; j++) { + const point = new cv.Point(i, j); + if (cv.pointPolygonTest(contour, point, false) >= 0) { + mask.data[(point.y * mask.cols + point.x)] = 255; + } + } + } + + const erode_kernel = new cv.Mat(); + cv.Mat.ones(size, size, cv.CV_8U).copyTo(erode_kernel); + + const dilate = new cv.Mat(); + cv.dilate(mask, dilate, erode_kernel, new cv.Point(-1,-1), iter); + this.showmatImg(dilate, document.querySelector('.processed-image-container')); + + let contours = new cv.MatVector(); + let hierarchy = new cv.Mat(); + cv.findContours(dilate, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + if (contours.size() === 1) { + return contours.get(0); + } + return null; } /** @@ -225,7 +277,7 @@ class mlTools { * @param expansion {number} expansion percentage from existing data * @return {number[][]} processed polygon */ - applyDrawNoModel(polygon, threshold, expansion) { + applyDrawNoModel(polygon, threshold, expansion, kernel_size, iteration) { // remove last point from the polygon polygon.pop(); @@ -236,14 +288,13 @@ class mlTools { const expansionBound = this.getExpansionCoordicate(polygonBound, expansion); // get contours from detect edges image - const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, expansionBound.h, threshold); + const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, expansionBound.h, threshold, kernel_size, iteration); // re-align polygon origin polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); // get most fit contour - let fitContour = this.mostFitContour(contours, polygon, expansionBound); - console.log('fitContour: ', fitContour); + let fitContour = this.mostFitContour(contours, polygon, expansionBound, kernel_size, iteration); // re-align the most fit contour fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); @@ -263,6 +314,10 @@ class mlTools { * @return {Promise} */ loadModel(key) { + if (key === 'watershed') { + this.model = 'watershed'; + return Promise.resolve(true); + } return new Promise((resolve, reject) => { try { if (this.model) { @@ -393,8 +448,7 @@ class mlTools { * @param {number} threshold - upper threshold value for canny detection * @param {number} expansion - expansion percentage */ - async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion) { - console.log('applyDrawModel'); + async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion, kernel_size, iteration) { // remove last point from the polygon polygon.pop(); @@ -406,7 +460,6 @@ class mlTools { // get grid coordinate with grid size is model size const gridBounds = this.getGridCoordinate(size, expansionBound); - console.log('gridBounds: ', gridBounds); // loop over all pieces of image and run the model this.fullPredict.getContext('2d').clearRect(0, 0, this.fullPredict.width, this.fullPredict.height); @@ -415,31 +468,38 @@ class mlTools { for (let i = 0; i < gridBounds.length; i++) { // get image data const imgCanvasData = this.context.getImageData(gridBounds[i].x, gridBounds[i].y, size, size); - console.log('imgCanvasData: ', imgCanvasData); let val; tf.tidy(() => { const img = tf.browser.fromPixels(imgCanvasData).toFloat(); - console.log('img: ', img); - // const channedProcessedImg = this.channelProcessing(img, ch); - // console.log('channedProcessedImg: ', channedProcessedImg); - // const pixelScaledImg = this.pixelScaling(channedProcessedImg, scaleMethod); - // console.log('pixelScaledImg: ', pixelScaledImg); let img2; if (ch == 1) { img2 = tf.image.resizeBilinear(img, [size, size]).mean(2); } else { img2 = tf.image.resizeBilinear(img, [size, size]); } - console.log('img2: ', img2); - const batched = img2.reshape([1, size, size, ch]); - console.log('batched: ', batched); + let normalized; + if (scaleMethod == 'norm') { + const scale = tf.scalar(255); + normalized = img2.div(scale); + } else if (scaleMethod == 'center') { + const mean = img2.mean(); + normalized = img2.sub(mean); + } else if (scaleMethod == 'std') { + const mean = img2.mean(); + const std = (img2.squaredDifference(mean).sum()).div(img2.flatten().shape).sqrt(); + normalized = img2.sub(mean).div(std); + } else { + normalized = img2; + } + const batched = normalized.reshape([1, size, size, ch]); let values = model.predict(batched).dataSync(); - console.log('values: ', values); values = Array.from(values); // scale values values = values.map((x) => x * 255); val = []; while (values.length > 0) val.push(values.splice(0, size)); + const padding = 2; + val = this.fillBoundary(val, padding); }) tf.engine().startScope(); await tf.browser.toPixels(val, this.temp); @@ -447,18 +507,18 @@ class mlTools { tf.engine().endScope(); } - this.showCanvas(this.fullPredict, document.querySelector('#edge-img')); - const fullPredictCanvas = this.fullPredict.getContext('2d'); // get contours from detect edges image - const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, threshold, fullPredictCanvas, false); + const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, threshold, kernel_size, iteration, fullPredictCanvas, false); + + this.showCanvas(this.fullPredict, document.querySelector('.model-predict-image-container')); // re-align polygon origin polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); // get most fit contour - let fitContour = this.mostFitContour(contours, polygon, expansionBound); + let fitContour = this.mostFitContour(contours, polygon, expansionBound, kernel_size, iteration); // re-align the most fit contour fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); @@ -472,16 +532,36 @@ class mlTools { return fitContour; } - async applyDraw(polygon, threshold, expansion, scaleMethod = 'no_scale') { - console.log('model: ', this.model); + async applyDraw(polygon, threshold, expansion, kernel_size, iteration, scaleMethod = 'no_scale') { if (this.model && this.model !== 'watershed') { - return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, expansion); + return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, expansion, kernel_size, iteration); } else { - return this.applyDrawNoModel(polygon, threshold, expansion) + return this.applyDrawNoModel(polygon, threshold, expansion, kernel_size, iteration) + } + } + + fillBoundary(imageArray, padding) { + const size = imageArray.length; + for (let i = 0; i < padding; i++) { + for (let j = padding; j (elt.offsetHeight/elt.offsetWidth)) { + edgesCanvas.style.height = '100%'; + edgesCanvas.style.width = ''; + } else { + edgesCanvas.style.width = '100%'; + edgesCanvas.style.height = ''; + } // Append the canvas to the document body or any other container elt.appendChild(edgesCanvas); } showCanvas(canvas, elt) { empty(elt); + if ((canvas.height/canvas.width) > (elt.offsetHeight/elt.offsetWidth)) { + canvas.style.height = '100%'; + canvas.style.width = ''; + } else { + canvas.style.width = '100%'; + canvas.style.height = ''; + } elt.appendChild(canvas); } } diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index 83ea8dbaa..d535b87ef 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -1037,7 +1037,7 @@ * @private * __endNewFeature create a new feature data. */ - __endNewFeature: async function() { + __endNewFeature: async function(modifying = false) { if (this.drawMode == 'point') { this._current_path_.properties.style.color = this.style.color; this._current_path_.properties.style.lineJoin = this.style.lineJoin; @@ -1072,6 +1072,12 @@ ); return; } + if (!modifying) { + this.currentOriginPath = JSON.parse(JSON.stringify(this._current_path_)); + } else { + this._current_path_ = JSON.parse(JSON.stringify(this.currentOriginPath)); + } + console.log('this._current_path_: ', this._current_path_); if ( !this._current_path_ || this._current_path_.geometry.coordinates[0].length < 2 || @@ -1080,10 +1086,12 @@ return; } // click on canvas // set style and drawing model - this._current_path_.properties.style.color = this.style.color; - this._current_path_.properties.style.lineJoin = this.style.lineJoin; - this._current_path_.properties.style.lineCap = this.style.lineCap; - this._current_path_.properties.style.isFill = this.style.isFill; + try { + this._current_path_.properties.style.color = this.style.color; + this._current_path_.properties.style.lineJoin = this.style.lineJoin; + this._current_path_.properties.style.lineCap = this.style.lineCap; + this._current_path_.properties.style.isFill = this.style.isFill; + } catch (error) {} let points = this._current_path_.geometry.coordinates[0]; /* Modifications */ @@ -1100,16 +1108,23 @@ // align points = this.__align(points); - const mlPoints = await this.__mlDraw(points); - points = mlPoints; + if ($UI.AssistantViewer.__isEnableAssistant()) { + const mlPoints = await this.__mlDraw(points); + points = mlPoints; + } // simplify and postprocess if(this.isMoving) spen.smoothness = spen.s; if (!(this.drawMode === 'grid') && this._simplify) if(spen.mode != 0) points = mtool.populate(points, 500000, ~~this.scaleWindowtoImage(2), 150); - else - points = simplify(points, 3.5); + else { + if ($UI.AssistantViewer.__isEnableAssistant()) { + points = simplify(points, $UI.AssistantViewer.__getSettingModes.roughness); + } else { + points = simplify(points, 3.5); + } + } // float to integer points = this._convert_integer(points); @@ -1165,6 +1180,9 @@ this._current_path_.geometry.coordinates[0], ); + if (modifying) { + this._path_index--; + } if (this._path_index < this._draws_data_.length) { this._draws_data_ = this._draws_data_.slice(0, this._path_index); } @@ -1400,11 +1418,13 @@ }, /** - * Align functions + * Machine learning draw functions *@param {points} *@return {points} */ __mlDraw: async function(points) { + const {radius, threshold, kernel_size, iteration} = $UI.AssistantViewer.__getSettingModes(); + const scaleMethod = $UI.AssistantViewer.__getScaleMethod(); var dist = new Array(); var ol = points; for (i = 0; i < ol.length; i++) { @@ -1412,7 +1432,7 @@ dist[i] = this._viewer.viewport.imageToWindowCoordinates(dist[i]); dist[i] = [Math.floor(dist[i].x * this.align_fx), Math.floor(dist[i].y * this.align_fy)]; } - dist = await mltools.applyDraw(dist, 70, 30); + dist = await mltools.applyDraw(dist, threshold, radius, kernel_size, iteration, scaleMethod); for (i = 0; i < dist.length; i++) { dist[i] = new OpenSeadragon.Point(dist[i][0] / this.align_fx, dist[i][1] / this.align_fy); dist[i] = this._viewer.viewport.windowToImageCoordinates(dist[i]); From c9585a4c9353abde2b705f67d4fc74e75fc59dd2 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Wed, 12 Jul 2023 19:23:35 +0700 Subject: [PATCH 11/46] Remove console log --- core/extension/openseadragon-canvas-draw-overlay.js | 1 - 1 file changed, 1 deletion(-) diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index d535b87ef..be43bcc7d 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -1077,7 +1077,6 @@ } else { this._current_path_ = JSON.parse(JSON.stringify(this.currentOriginPath)); } - console.log('this._current_path_: ', this._current_path_); if ( !this._current_path_ || this._current_path_.geometry.coordinates[0].length < 2 || From e1cd0a7d828b5bcf8ae5ea59f0c3aa1bf2456c4e Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Sun, 23 Jul 2023 08:35:02 +0700 Subject: [PATCH 12/46] optimize machine learning assistant and add multi annotation function --- apps/viewer/init.js | 2 +- apps/viewer/uicallbacks.js | 1 + apps/viewer/viewer.html | 3 +- components/ml-assistant/ml-assistant.js | 130 +++---- components/ml-assistant/ml-tool.js | 338 ++++++++++++------ .../openseadragon-canvas-draw-overlay.js | 177 ++++----- 6 files changed, 382 insertions(+), 269 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index cecc331e0..edba53962 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -855,7 +855,7 @@ async function initUIcomponents() { width: 240, contentPadding: 5, position: 'right', - height: '71vh', + height: '50vh', top: '30px' }); diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index 95f79a003..91462d694 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -335,6 +335,7 @@ function annotationOn(state, target) { canvasDraw.style.color = style.color; li.appendChild(label); + $UI.AssistantViewer.undoBtn.onclick=()=>canvasDraw.__align_undo(); switch (state) { case 1: spen.menu(65, 0.2); diff --git a/apps/viewer/viewer.html b/apps/viewer/viewer.html index d9074b610..5f2b762fb 100644 --- a/apps/viewer/viewer.html +++ b/apps/viewer/viewer.html @@ -178,6 +178,7 @@ + @@ -422,8 +423,6 @@
- -
diff --git a/components/ml-assistant/ml-assistant.js b/components/ml-assistant/ml-assistant.js index c71ad69ea..10b3562fd 100644 --- a/components/ml-assistant/ml-assistant.js +++ b/components/ml-assistant/ml-assistant.js @@ -1,22 +1,3 @@ -// Proposal: Control panel for machine learning assistant -// Include: -// + Add model -> Open add model modal -// + Model enable -// + Model selection (Options: watershed, smartpen, ...) -// + Pixel scaling selection (4 options: No Scaling, Normalization, Centerization, Standardization) -// + Model info -> Open model info modal - -// + Annotate Mode zone (checkbox) -// - Draw -// - Click -// - ROI - -// + Setting Zone (input range) -// - Radius -// - Threshold -// - Roughness - - function Assistant(options) { this.className = 'Assistant'; this.setting = { @@ -70,6 +51,7 @@ Assistant.prototype.__refreshUI = async function() { const modelSelectionId = randomId(); const pixelScalingId = randomId(); const modelInfoId = randomId(); + const undoInfoId = randomId(); // Pixel Scaling ID const noScaleId = randomId(); @@ -115,11 +97,13 @@ Assistant.prototype.__refreshUI = async function() {
  • info
  • +
  • + undo +
  • - - - + +
    @@ -129,31 +113,17 @@ Assistant.prototype.__refreshUI = async function() {
    Threshold
    - - 90 + + 70
    -
    -
    Roughness
    - - 4 +
    +
    Min Overlap
    + + 10
    -
    -
    Kernel Size
    - - 0 -
    -
    -
    Iteration
    - - 1 -
    -
    -
    - -
    - +
    `; @@ -165,6 +135,7 @@ Assistant.prototype.__refreshUI = async function() { this.enableBtn = this.view.querySelector(`#${modelEnableId}`); this.modelSelectionBtn = this.view.querySelector(`#${modelSelectionId}`); this.infoBtn = this.view.querySelector(`#${modelInfoId}`); + this.undoBtn = this.view.querySelector(`#${undoInfoId}`); this.modelList = this.view.querySelector('.model-list'); this.pixelScaleList = this.view.querySelector('.pixel-scale-list') @@ -172,11 +143,32 @@ Assistant.prototype.__refreshUI = async function() { this.settingZone = { radius: this.view.querySelector('.radius-setting'), threshold: this.view.querySelector('.threshold-setting'), - roughness: this.view.querySelector('.roughness-setting'), - kernel_size: this.view.querySelector('.separation-kernel-size-setting'), - iteration: this.view.querySelector('.separation-iteration-setting'), + overlap: this.view.querySelector('.min-overlap-setting'), } - this.processedImgContainer = this.view.querySelector('.processed-image-container'), + + const radiusLabel = this.settingZone.radius.querySelector('pre'); + tippy(radiusLabel, { + content: 'Enhance the coordinate percentage relative to the polygon drawn by the user. These expanded image will be used as input for image processing.', + placement: 'left', + delay: 300, + theme: 'translucent', + }); + + const thresholdLabel = this.settingZone.threshold.querySelector('pre'); + tippy(thresholdLabel, { + content: 'The separation threshold value represents the distinction between the object (foreground) and the surrounding area (background).', + placement: 'left', + delay: 300, + theme: 'translucent', + }); + + const overlapLabel = this.settingZone.overlap.querySelector('pre'); + tippy(overlapLabel, { + content: 'The minimum overlap refers to the required intersection between the user-selected polygon and the predicted polygons.', + placement: 'left', + delay: 300, + theme: 'translucent', + }); this.modelPredictImgContainer = this.view.querySelector('.model-predict-image-container'), await this.__createModelList(); @@ -228,16 +220,17 @@ Assistant.prototype.__assignEventListener = function() { elt.addEventListener('click', (event) => { this.annotateModeZone.querySelectorAll('input').forEach((iElt) => { iElt.checked = false; + iElt.removeAttribute('checked'); }) event.target.checked = true; + event.target.setAttribute('checked', 'true'); }) }) // Change radius event this.settingZone.radius.querySelector('input').addEventListener('change', (event) => { this.settingZone.radius.querySelector('span').textContent = event.target.value; - // TODO process ROI processing - if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + if (this.__isEnableAssistant()) { this._viewer.raiseEvent('ml-draw-setting-change', {}); } }) @@ -245,35 +238,15 @@ Assistant.prototype.__assignEventListener = function() { // Change threshold event this.settingZone.threshold.querySelector('input').addEventListener('change', (event) => { this.settingZone.threshold.querySelector('span').textContent = event.target.value; - // TODO process ROI processing - if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { - this._viewer.raiseEvent('ml-draw-setting-change', {}); - } - }) - - // Change roughness event - this.settingZone.roughness.querySelector('input').addEventListener('change', (event) => { - this.settingZone.roughness.querySelector('span').textContent = event.target.value; - // TODO process ROI processing - if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { - this._viewer.raiseEvent('ml-draw-setting-change', {}); - } - }) - - // Change separation kernel size event - this.settingZone.kernel_size.querySelector('input').addEventListener('change', (event) => { - this.settingZone.kernel_size.querySelector('span').textContent = event.target.value; - // TODO process ROI processing - if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + if (this.__isEnableAssistant()) { this._viewer.raiseEvent('ml-draw-setting-change', {}); } }) - // Change separation iteration event - this.settingZone.iteration.querySelector('input').addEventListener('change', (event) => { - this.settingZone.iteration.querySelector('span').textContent = event.target.value; - // TODO process ROI processing - if (this.__isEnableAssistant() && this.__getAssistantMode() === 'annotateByDraw') { + // Change overlap event + this.settingZone.overlap.querySelector('input').addEventListener('change', (event) => { + this.settingZone.overlap.querySelector('span').textContent = event.target.value; + if (this.__isEnableAssistant()) { this._viewer.raiseEvent('ml-draw-setting-change', {}); } }) @@ -284,8 +257,8 @@ Assistant.prototype.__createModelList = async function() { const dropDownList = [ { icon: 'timeline', - title: 'Watershed', - value: 'watershed', + title: 'Default', + value: 'default', checked: true, }]; @@ -370,9 +343,7 @@ Assistant.prototype.__getSettingModes = function() { const settingMode = { radius: parseFloat(this.settingZone.radius.querySelector('input').value), threshold: parseFloat(this.settingZone.threshold.querySelector('input').value), - roughness: parseFloat(this.settingZone.roughness.querySelector('input').value), - kernel_size: parseFloat(this.settingZone.kernel_size.querySelector('input').value), - iteration: parseFloat(this.settingZone.iteration.querySelector('input').value), + overlap: parseFloat(this.settingZone.overlap.querySelector('input').value), } return settingMode; } @@ -384,6 +355,5 @@ Assistant.prototype.__getScaleMethod = function() { Assistant.prototype.__createElementFromHTML= function(htmlString) { var div = document.createElement('div'); div.innerHTML = htmlString.trim(); - // Change this to div.childNodes to support multiple top-level nodes return div.firstChild; }; diff --git a/components/ml-assistant/ml-tool.js b/components/ml-assistant/ml-tool.js index 246e59164..6638e6e8f 100644 --- a/components/ml-assistant/ml-tool.js +++ b/components/ml-assistant/ml-tool.js @@ -20,6 +20,8 @@ class mlTools { this.undo; this.temp = document.createElement('canvas'); this.fullPredict = document.createElement('canvas'); + this.sureFgImg1 = null; + this.sureFgImg2 = null; } initcanvas(canvas) { @@ -47,7 +49,7 @@ class mlTools { let thresholdImg1 = new cv.Mat(); cv.threshold(gray, thresholdImg1, th, 255, cv.THRESH_BINARY) const sureFgImg1 = this.thresholdImgToForegroundImg(thresholdImg1, size, iter, 2); - this.showmatImg(sureFgImg1, document.querySelector('.processed-image-container')); + let contours1 = new cv.MatVector(); let hierarchy1 = new cv.Mat(); cv.findContours(sureFgImg1, contours1, hierarchy1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); @@ -59,9 +61,24 @@ class mlTools { let contours2 = new cv.MatVector(); let hierarchy2 = new cv.Mat(); cv.findContours(sureFgImg2, contours2, hierarchy2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + thresholdImg2.delete(); + hierarchy2.delete(); + sureFgImg2.delete(); + thresholdImg1.delete(); + hierarchy1.delete(); + sureFgImg1.delete(); + img.delete(); + gray.delete(); + return [contours1, contours2] } + thresholdImg1.delete(); + hierarchy1.delete(); + sureFgImg1.delete(); + img.delete(); + gray.delete(); + return [contours1]; } @@ -76,7 +93,11 @@ class mlTools { const erode = new cv.Mat(); const erode_kernel = new cv.Mat(); cv.Mat.ones(erode_size, erode_size, cv.CV_8U).copyTo(erode_kernel); - cv.erode(morph, erode, erode_kernel, new cv.Point(-1,-1), iteration) + cv.erode(morph, erode, erode_kernel, new cv.Point(-1,-1), iteration); + kernel.delete(); + opening.delete(); + morph.delete(); + erode_kernel.delete(); return erode; } @@ -86,7 +107,7 @@ class mlTools { * @param polygon {number[][]} polygon data * @return {number} overlap area */ - overlapArea(contour, polygon) { + overlapAreaAndCircumference(contour, polygon) { const contour2 = contour.slice(); const polygon2 = polygon.slice(); contour2.push(contour2[0]); @@ -94,12 +115,22 @@ class mlTools { const contourTurf = turf.polygon([contour2]); const polygonTurf = turf.polygon([polygon2]); const intersection = turf.intersect(contourTurf, polygonTurf); - if (!intersection) { return 0.0; } - - return turf.area(intersection); + const intersectionPolygon = intersection.geometry.coordinates[0]; + return { + area: this.polygonArea(intersectionPolygon), + circumference: this.getCircumference(intersectionPolygon), + }; + } + + getCircumference(polygon) { + let length = 0; + for (let i = 0; i < polygon.length - 1; i++) { + length += Math.sqrt((polygon[i][0] - polygon[i+1][0])**2 + (polygon[i][1] - polygon[i+1][1])**2) + } + return length; } /** @@ -117,51 +148,121 @@ class mlTools { } /** - * Find the most fit contour with polygon data - * @param contours {any} openCV contours data - * @param polygon {number[][]} polygon data - * @param w {number} width of original image - * @param h {number} height of orignal image - * @return {number[][]} the most fit contour data array + * find the most fit contours with user draw polygon + * @param {*} contours + * @param {*} polygon + * @param {*} expansionBound + * @param {*} erode_size + * @param {*} iteration + * @param {*} overlap + * @returns {number[][]} contour Array */ - mostFitContour(contours, polygon, expansionBound, erode_size = 2, iteration = 1) { + mostFitContour(contours, polygon, expansionBound, erode_size = 2, iteration = 1, overlap = 30) { let maxArea = 0; - let area; let fitContour; let expandedContour; + const polygonArea = this.polygonArea(polygon); for (let j = 0; j < contours.length; j++) { for (let i = 0; i < contours[j].size(); i++) { let contour = contours[j].get(i); - if (cv.contourArea(contour, false) < 1) { + if (cv.contourArea(contour, false) < 50) { continue; } const contourArray = this.convertToArray(contour); if (this.closeBoundary(contourArray, expansionBound, 3)) { continue; } - area = this.overlapArea(contourArray, polygon); + const { area } = this.overlapAreaAndCircumference(contourArray, polygon); + if (area < 50) { + continue; + } if (area > maxArea) { maxArea = area; - fitContour = contour; + if (fitContour) { + fitContour.delete(); + } + fitContour = contour.clone(); } + contour.delete(); } + contours[j].delete(); } if (fitContour) { expandedContour = this.expandContour(fitContour, expansionBound.w, expansionBound.h, erode_size, iteration); + const fitContourArray = this.convertToArray(expandedContour); + expandedContour.delete(); + const expaned = this.overlapAreaAndCircumference(fitContourArray, polygon); + if (expaned.area/polygonArea < overlap/100) { + return []; + } + return [fitContourArray]; + } else { + return []; + } + } + + manyFitContour(contours, polygon, expansionBound, erode_size = 2, iteration = 1, overlap = 30) { + let fitContours = []; + let expandedContours = []; + let expandedContourArrays = []; + let totalOverlapArea = 0; + const polygonArea = this.polygonArea(polygon); + for (let j = 0; j < contours.length; j++) { + for (let i = 0; i < contours[j].size(); i++) { + let contour = contours[j].get(i); + if (cv.contourArea(contour, false) < 50) { + continue; + } + const contourArray = this.convertToArray(contour); + if (this.closeBoundary(contourArray, expansionBound, 3)) { + continue; + } + const {area, circumference} = this.overlapAreaAndCircumference(contourArray, polygon); + if (!area || area < 15) { + continue; + } + const expanedArea = area + circumference*erode_size*iteration/2; + totalOverlapArea += expanedArea; + fitContours.push(contour.clone()); + contour.delete(); + } + contours[j].delete(); } - if (!expandedContour) { + if (!fitContours.length || totalOverlapArea/polygonArea < overlap/100) { return []; } - const fit_contour = this.convertToArray(expandedContour); - return fit_contour; + + expandedContours = fitContours.map((contour) => { + return this.expandContour(contour, expansionBound.w, expansionBound.h, erode_size, iteration); + }) + + expandedContourArrays = expandedContours.map((expanedContour) => { + const expandedContourArray = this.convertToArray(expanedContour); + expanedContour.delete(); + return expandedContourArray; + }) + + return expandedContourArrays; + } + + polygonArea(points) { + let area = 0; + let j = points.length - 2; + for (let i = 0; i < points.length - 1; i++) { + area += (points[j][0] + points[i][0]) * (points[j][1] - points[i][1]); + j = i; + } + return Math.abs(area / 2); } expandContour(contour, width, height, size, iter) { const mask = new cv.Mat.zeros(height, width, cv.CV_8UC1); + const point = new cv.Point(0, 0); for (let i = 0; i < width; i++) { for (let j = 0; j < height; j++) { - const point = new cv.Point(i, j); + point.x = i; + point.y = j; if (cv.pointPolygonTest(contour, point, false) >= 0) { mask.data[(point.y * mask.cols + point.x)] = 255; } @@ -173,14 +274,22 @@ class mlTools { const dilate = new cv.Mat(); cv.dilate(mask, dilate, erode_kernel, new cv.Point(-1,-1), iter); - this.showmatImg(dilate, document.querySelector('.processed-image-container')); + // const processImage = document.querySelector('.processed-image-container'); + // this.showmatImg(dilate, processImage); let contours = new cv.MatVector(); let hierarchy = new cv.Mat(); cv.findContours(dilate, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + mask.delete(); + erode_kernel.delete(); + dilate.delete(); + hierarchy.delete(); if (contours.size() === 1) { - return contours.get(0); + const resultContour = contours.get(0).clone(); + contours.delete(); + return resultContour; } + contours.delete(); return null; } @@ -277,7 +386,7 @@ class mlTools { * @param expansion {number} expansion percentage from existing data * @return {number[][]} processed polygon */ - applyDrawNoModel(polygon, threshold, expansion, kernel_size, iteration) { + applyDrawNoModel(polygon, threshold, expansion, overlap, drawMany = false) { // remove last point from the polygon polygon.pop(); @@ -286,26 +395,50 @@ class mlTools { // get expansion coordinate (left, top, width, height) const expansionBound = this.getExpansionCoordicate(polygonBound, expansion); - - // get contours from detect edges image - const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, expansionBound.h, threshold, kernel_size, iteration); - + // re-align polygon origin polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); - // get most fit contour - let fitContour = this.mostFitContour(contours, polygon, expansionBound, kernel_size, iteration); - - // re-align the most fit contour - fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + let fitContours; + for (let size = 0; size < 20; size+=6) { + for (let iter = 1; iter < 10; iter+=4) { + for (let dth = 1; dth < 100; dth+=10) { + const th = (dth % 2 === 1) ? threshold - ~~(dth/2) : threshold - ~~(dth/2); + if (th <= 0 || th >= 255) { + continue; + } + // get contours from detect edges image + const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, expansionBound.h, th, size, iter); + // get most fit contour + if (!drawMany) { + fitContours = this.mostFitContour(contours, polygon, expansionBound, size, iter, overlap); + } else { + fitContours = this.manyFitContour(contours, polygon, expansionBound, size, iter, overlap); + } + if (fitContours.length) { + break; + } + } + if (fitContours.length) { + break; + } + } + if (fitContours.length) { + break; + } + } - if (fitContour.length === 0) { + if (fitContours.length === 0) { return []; } - // add last point into the most fit contour - fitContour.push(fitContour[0]); - - return fitContour; + // re-align the most fit contour + fitContours = fitContours.map((fitContour) => { + fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + fitContour.push(fitContour[0]); + return fitContour; + }) + + return fitContours; } /** @@ -314,14 +447,19 @@ class mlTools { * @return {Promise} */ loadModel(key) { - if (key === 'watershed') { - this.model = 'watershed'; + if (key === 'default') { + try { + this.model.dispose(); + } catch (error) { } + this.model = 'default'; return Promise.resolve(true); } return new Promise((resolve, reject) => { try { - if (this.model) { - this.model.dispose(); + if (this.model && this.model !== 'default') { + try { + this.model.dispose(); + } catch (error) { } } const tx = db.transaction('models_store', 'readonly'); const store = tx.objectStore('models_store'); @@ -358,45 +496,6 @@ class mlTools { }) } - /** - * Make - * @param {any} img tensorflow data - * @param {number} ch - number of channel process by model (gray: 1, rgb: 4) - * @return {any} - process image data - */ - channelProcessing(img, ch=1) { - if (ch == 1) { - return tf.image.resizeBilinear(img, [imageSize, imageSize]).mean(2); - } else { - return tf.image.resizeBilinear(img, [imageSize, imageSize]); - } - } - - /** - * Scaling processing for model input images - * @param {any} img - image tensorflow data - * @param {string} scaleMethod - model scaling method - * @return {any} - scaled image data - */ - pixelScaling(img, scaleMethod) { - if (scaleMethod == 'no_scale') { - return img - } else if (scaleMethod == 'norm') { - // Pixel Normalization: scale pixel values to the range 0-1. - const scale = tf.scalar(255); - return img.div(scale); - } else if (scaleMethod == 'center') { - // Pixel Centering: scale pixel values to have a zero mean. - const mean = img.mean(); - return img.sub(mean); - } else { - // Pixel Standardization: scale pixel values to have a zero mean and unit variance. - const mean = img.mean(); - const std = (img.squaredDifference(mean).sum()).div(img.flatten().shape).sqrt(); - return img.sub(mean).div(std); - } - } - /** * Get expansion coordinate parameter coresponding with using model * @param {number} step - current model input size px @@ -448,7 +547,7 @@ class mlTools { * @param {number} threshold - upper threshold value for canny detection * @param {number} expansion - expansion percentage */ - async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion, kernel_size, iteration) { + async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion, overlap, drawMany = false) { // remove last point from the polygon polygon.pop(); @@ -481,25 +580,33 @@ class mlTools { if (scaleMethod == 'norm') { const scale = tf.scalar(255); normalized = img2.div(scale); + scale.dispose(); } else if (scaleMethod == 'center') { const mean = img2.mean(); normalized = img2.sub(mean); + mean.dispose(); } else if (scaleMethod == 'std') { const mean = img2.mean(); const std = (img2.squaredDifference(mean).sum()).div(img2.flatten().shape).sqrt(); normalized = img2.sub(mean).div(std); + mean.dispose(); + std.dispose(); } else { normalized = img2; } const batched = normalized.reshape([1, size, size, ch]); let values = model.predict(batched).dataSync(); - values = Array.from(values); + let valuesArray = Array.from(values); // scale values - values = values.map((x) => x * 255); + valuesArray = valuesArray.map((x) => x * 255); val = []; - while (values.length > 0) val.push(values.splice(0, size)); + while (valuesArray.length > 0) val.push(valuesArray.splice(0, size)); const padding = 2; val = this.fillBoundary(val, padding); + img.dispose(); + img2.dispose(); + normalized.dispose(); + batched.dispose(); }) tf.engine().startScope(); await tf.browser.toPixels(val, this.temp); @@ -507,36 +614,61 @@ class mlTools { tf.engine().endScope(); } - const fullPredictCanvas = this.fullPredict.getContext('2d'); - - // get contours from detect edges image - const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, threshold, kernel_size, iteration, fullPredictCanvas, false); - this.showCanvas(this.fullPredict, document.querySelector('.model-predict-image-container')); + const fullPredictCanvas = this.fullPredict.getContext('2d'); // re-align polygon origin polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); - // get most fit contour - let fitContour = this.mostFitContour(contours, polygon, expansionBound, kernel_size, iteration); - - // re-align the most fit contour - fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + let fitContours = []; + + for (let size = 0; size < 20; size+=6) { + for (let iter = 1; iter < 10; iter+=4) { + for (let dth = 0; dth < 200; dth+=10) { + const th = (dth % 2 === 1) ? threshold - ~~(dth/2) : threshold + ~~(dth/2); + if (th <= 0 || th >= 255) { + continue; + } + // get contours from detect edges image + const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, th, size, iter, fullPredictCanvas, false); + // get most fit contour + if (!drawMany) { + fitContours = this.mostFitContour(contours, polygon, expansionBound, size, iter, overlap); + } else { + fitContours = this.manyFitContour(contours, polygon, expansionBound, size, iter, overlap); + } + if (fitContours.length) { + break; + } + } + if (fitContours.length) { + break; + } + } + if (fitContours.length) { + break; + } + } - if (fitContour.length === 0) { + if (fitContours.length === 0) { return []; } - // add last point into the most fit contour - fitContour.push(fitContour[0]); - return fitContour; + // re-align the most fit contour + fitContours = fitContours.map((fitContour) => { + fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + fitContour.push(fitContour[0]); + return fitContour; + }); + + return fitContours; } - async applyDraw(polygon, threshold, expansion, kernel_size, iteration, scaleMethod = 'no_scale') { - if (this.model && this.model !== 'watershed') { - return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, expansion, kernel_size, iteration); + async applyDraw(polygon, threshold, expansion, overlap, scaleMethod = 'no_scale', drawMany = false) { + if (this.model && this.model !== 'default') { + return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, expansion, overlap, drawMany); } else { - return this.applyDrawNoModel(polygon, threshold, expansion, kernel_size, iteration) + return this.applyDrawNoModel(polygon, threshold, expansion, overlap, drawMany); } } diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index be43bcc7d..9014a2119 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -1106,88 +1106,88 @@ // align points = this.__align(points); - + let pointsList; if ($UI.AssistantViewer.__isEnableAssistant()) { const mlPoints = await this.__mlDraw(points); - points = mlPoints; + pointsList = mlPoints; + } else { + pointsList = [points]; } - - // simplify and postprocess - if(this.isMoving) spen.smoothness = spen.s; - if (!(this.drawMode === 'grid') && this._simplify) - if(spen.mode != 0) - points = mtool.populate(points, 500000, ~~this.scaleWindowtoImage(2), 150); - else { - if ($UI.AssistantViewer.__isEnableAssistant()) { - points = simplify(points, $UI.AssistantViewer.__getSettingModes.roughness); - } else { - points = simplify(points, 3.5); + if (modifying) { + this._path_index -= this.lastPointsListLength; + } + this.lastPointsListLength = pointsList.length; + for (let i = 0; i < pointsList.length; i++) { + // simplify and postprocess + drawPoints = pointsList[i]; + if(this.isMoving) spen.smoothness = spen.s; + if (!(this.drawMode === 'grid') && this._simplify) + if(spen.mode != 0) + drawPoints = mtool.populate(drawPoints, 500000, ~~this.scaleWindowtoImage(2), 150); + else { + drawPoints = simplify(drawPoints, 3.5); } - } - - // float to integer - points = this._convert_integer(points); - - if (!(this.drawMode === 'line' || this.drawMode == 'grid')) { - let isIntersect = false; - if (isSelfIntersect(points)) { - isIntersect = true; - console.info('The polygon just drawn has an intersection.'); - if (! window.localStorage.getItem('_intersect_warn')) { - alert( - 'A Self-Intersecting Polygon Will Cause Inaccurate Area and Circumference.', + + // float to integer + drawPoints = this._convert_integer(drawPoints); + + if (!(this.drawMode === 'line' || this.drawMode == 'grid')) { + let isIntersect = false; + if (isSelfIntersect(drawPoints)) { + isIntersect = true; + console.info('The polygon just drawn has an intersection.'); + if (! window.localStorage.getItem('_intersect_warn')) { + alert( + 'A Self-Intersecting Polygon Will Cause Inaccurate Area and Circumference.', + ); + console.info('Firing Intersect user Alert just this once.'); + window.localStorage.setItem('_intersect_warn', 'true'); + } + } + let sqmpsqp = null; // square microns per square pixels + if ( + this._viewer.mpp_x && + this._viewer.mpp_y && + this._viewer.mpp_x != 1e9 && + this._viewer.mpp_y != 1e9 + ) { + sqmpsqp = this._viewer.mpp_x * this._viewer.mpp_y; + // calculate the are of polygon + this._current_path_.properties.area = + sqmpsqp * polygonArea(drawPoints); + this._current_path_.properties.circumference = getCircumference( + drawPoints, + this._viewer.mpp_x, + this._viewer.mpp_y, + ); + this._current_path_.properties.isIntersect = isIntersect; + } else if (this._viewer.mpp && this._viewer.mpp != 1e9) { + sqmpsqp = this._viewer.mpp * this._viewer.mpp; + // calculate the are of polygon + this._current_path_.properties.area = + sqmpsqp * polygonArea(drawPoints); + this._current_path_.properties.circumference = getCircumference( + drawPoints, + this._viewer.mpp_x, + this._viewer.mpp_y, ); - console.info('Firing Intersect user Alert just this once.'); - window.localStorage.setItem('_intersect_warn', 'true'); + this._current_path_.properties.isIntersect = isIntersect; + } else { + this._current_path_.properties.nommp = true; } } - let sqmpsqp = null; // square microns per square pixels - if ( - this._viewer.mpp_x && - this._viewer.mpp_y && - this._viewer.mpp_x != 1e9 && - this._viewer.mpp_y != 1e9 - ) { - sqmpsqp = this._viewer.mpp_x * this._viewer.mpp_y; - // calculate the are of polygon - this._current_path_.properties.area = - sqmpsqp * polygonArea(points); - this._current_path_.properties.circumference = getCircumference( - points, - this._viewer.mpp_x, - this._viewer.mpp_y, - ); - this._current_path_.properties.isIntersect = isIntersect; - } else if (this._viewer.mpp && this._viewer.mpp != 1e9) { - sqmpsqp = this._viewer.mpp * this._viewer.mpp; - // calculate the are of polygon - this._current_path_.properties.area = - sqmpsqp * polygonArea(points); - this._current_path_.properties.circumference = getCircumference( - points, - this._viewer.mpp_x, - this._viewer.mpp_y, - ); - this._current_path_.properties.isIntersect = isIntersect; - } else { - this._current_path_.properties.nommp = true; - } - } - this._current_path_.geometry.coordinates[0] = points; - // create bounds - this._current_path_.bound.coordinates[0] = getBounds( - this._current_path_.geometry.coordinates[0], - ); + this._current_path_.geometry.coordinates[0] = drawPoints; + // create bounds + this._current_path_.bound.coordinates[0] = getBounds( + this._current_path_.geometry.coordinates[0], + ); - if (modifying) { - this._path_index--; - } - if (this._path_index < this._draws_data_.length) { - this._draws_data_ = this._draws_data_.slice(0, this._path_index); + if (this._path_index < this._draws_data_.length) { + this._draws_data_ = this._draws_data_.slice(0, this._path_index); + } + this._draws_data_.push(Object.assign({}, JSON.parse(JSON.stringify(this._current_path_)))); + this._path_index++; } - - this._draws_data_.push(Object.assign({}, this._current_path_)); - this._path_index++; this._current_path_ = null; DrawHelper.clearCanvas(this._draw_); this._display_ctx_.lineWidth = this.style.lineWidth; @@ -1422,24 +1422,35 @@ *@return {points} */ __mlDraw: async function(points) { - const {radius, threshold, kernel_size, iteration} = $UI.AssistantViewer.__getSettingModes(); + const {radius, threshold, overlap} = $UI.AssistantViewer.__getSettingModes(); const scaleMethod = $UI.AssistantViewer.__getScaleMethod(); - var dist = new Array(); + var dist = []; + var dists; var ol = points; for (i = 0; i < ol.length; i++) { dist.push(new OpenSeadragon.Point(ol[i][0], ol[i][1])); dist[i] = this._viewer.viewport.imageToWindowCoordinates(dist[i]); dist[i] = [Math.floor(dist[i].x * this.align_fx), Math.floor(dist[i].y * this.align_fy)]; } - dist = await mltools.applyDraw(dist, threshold, radius, kernel_size, iteration, scaleMethod); - for (i = 0; i < dist.length; i++) { - dist[i] = new OpenSeadragon.Point(dist[i][0] / this.align_fx, dist[i][1] / this.align_fy); - dist[i] = this._viewer.viewport.windowToImageCoordinates(dist[i]); - dist[i].x = Math.floor(dist[i].x); - dist[i].y = Math.floor(dist[i].y); - points[i] = [dist[i].x, dist[i].y]; + let drawMany; + console.log('mode: ',$UI.AssistantViewer.__getAssistantMode()); + if ($UI.AssistantViewer.__getAssistantMode() === 'annotateOneByDraw') { + drawMany = false; + } else if ($UI.AssistantViewer.__getAssistantMode() === 'annotateManyByDraw') { + drawMany = true; } - return points; + dists = await mltools.applyDraw(dist, threshold, radius, overlap, scaleMethod, drawMany); + return dists.map((dist) => { + const distAlign = []; + for (i = 0; i < dist.length; i++) { + dist[i] = new OpenSeadragon.Point(dist[i][0] / this.align_fx, dist[i][1] / this.align_fy); + dist[i] = this._viewer.viewport.windowToImageCoordinates(dist[i]); + dist[i].x = Math.floor(dist[i].x); + dist[i].y = Math.floor(dist[i].y); + distAlign.push([dist[i].x, dist[i].y]); + } + return distAlign; + }); } }; From e1f760e0bfea26a7394b324521126a2804dcbe1c Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 24 Jul 2023 13:40:13 +0700 Subject: [PATCH 13/46] Fix add add modal modal and modal info load method --- apps/viewer/init.js | 173 ++++++++++++++++++++++------------------ apps/viewer/viewer.html | 1 + 2 files changed, 95 insertions(+), 79 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index edba53962..0cd1e7c40 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -741,80 +741,7 @@ async function initUIcomponents() { ], }); - // Create uploadModal for model uploads. - $UI.uploadModal = new ModalBox({ - id: 'upload_panel', - hasHeader: true, - headerText: 'Upload Model', - hasFooter: false, - provideContent: true, - content: ` -
    -
      -
    • - - - Name of the model -
    • -
    • - - - The image size on which the model is trained (y x y) -
    • -
      - -
      - - -
    • - - - Magnification of input images -
    • -
      - -

      -
      Select model.json first followed by the weight binaries.

      -

      -

      -
      URL to the ModelAndWeightsConfig JSON describing the model.

      -

      - -
    - -
    - - - `, - }); - - // Create infoModal to show information about models uploaded. - $UI.infoModal = new ModalBox({ - id: 'model_info', - hasHeader: true, - headerText: 'Available Models', - hasFooter: false, - provideContent: true, - content: ` - - - - - - - - - - - - -
    NameInput SizeSize (MB)Date SavedRemove Model
    - `, - }); + initModelModals(); // TODO -- labels // $UI.labelsSideMenu = new SideMenu({ @@ -1046,6 +973,83 @@ async function initUIcomponents() { }) } +function initModelModals() { + // Create uploadModal for model uploads. + $UI.uploadModal = new ModalBox({ + id: 'upload_panel', + hasHeader: true, + headerText: 'Upload Model', + hasFooter: false, + provideContent: true, + content: ` +
    +
      +
    • + + + Name of the model +
    • +
    • + + + The image size on which the model is trained (y x y) +
    • +
      + +
      + + +
    • + + + Magnification of input images +
    • +
      + +

      +
      Select model.json first followed by the weight binaries.

      +

      +

      +
      URL to the ModelAndWeightsConfig JSON describing the model.

      +

      + +
    + +
    + + + `, + }); + + // Create infoModal to show information about models uploaded. + $UI.infoModal = new ModalBox({ + id: 'model_info', + hasHeader: true, + headerText: 'Available Models', + hasFooter: false, + provideContent: true, + content: ` + + + + + + + + + + + + +
    NameInput SizeSize (MB)Date SavedRemove Model
    + `, + }); +} + // Shows the uploaded models' details async function showInfo() { var data = await tf.io.listModels(); @@ -1067,7 +1071,9 @@ async function showInfo() { if (name.slice(0, 3) == 'seg') { store.get(name).onsuccess = function(e) { - inputShape = e.target.result.input_shape.slice(1, 3).join('x'); + try { + inputShape = e.target.result.input_shape.slice(1, 3).join('x'); + } catch (error) {} td = row.insertCell(); td.innerText = name.split('_').splice(1).join('_').slice(0, -3); td = row.insertCell(); @@ -1119,7 +1125,7 @@ async function deleteModel(name) { }, 3000); } $UI.infoModal.close(); - initUIcomponents(); + initModelModals(); } } } else { @@ -1127,7 +1133,16 @@ async function deleteModel(name) { } } -function uploadModel() { +async function uploadModel() { + modelName = []; + Object.keys(await tf.io.listModels()).forEach(function(element) { + const value = element.split('/').pop(); + if (value.slice(0, 3) == 'seg') { + const title = element.split('/').pop().split('_')[1].slice(0, -3); + console.log() + modelName.push(title); + } + }); var _name = document.querySelector('#name'); var _imageSize = document.querySelector('#imageSize'); var mag = document.querySelector('#magnification'); @@ -1155,7 +1170,7 @@ function uploadModel() { }); refresh.addEventListener('click', () => { - initUIcomponents(); + initModelModals(); }); submit.addEventListener('click', async function(e) { @@ -1228,7 +1243,7 @@ function uploadModel() { }, 3000); } $UI.uploadModal.close(); - initUIcomponents(); + initModelModals(); }; req.onerror = function(e) { status.innerText = 'Some error this way!'; diff --git a/apps/viewer/viewer.html b/apps/viewer/viewer.html index 5f2b762fb..522ab8cd5 100644 --- a/apps/viewer/viewer.html +++ b/apps/viewer/viewer.html @@ -423,6 +423,7 @@
    + From df694286b766fd391bbb1e0394988315b9107bd3 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 24 Jul 2023 14:27:11 +0700 Subject: [PATCH 14/46] Fix asynchornous in endNewFeature function --- .../openseadragon-canvas-draw-overlay.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index 9014a2119..f7a0bccaa 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -458,7 +458,9 @@ this.align_fx = this._viewer.drawer.canvas.height/this._display_.height; // ml tools - mltools.initcanvas(this._viewer.drawer.canvas); + try { + mltools.initcanvas(this._viewer.drawer.canvas); + } catch (error) {} if ( 0 > img_point.x || this.imgWidth < img_point.x || @@ -602,7 +604,7 @@ } }, - pointClick: function(e) { + pointClick: async function(e) { this.raiseEvent('start-drawing', {originalEvent: e}); if (this.stop) { @@ -638,7 +640,7 @@ new OpenSeadragon.Point(line[0][0], line[0][1]), new OpenSeadragon.Point(line[line.length - 1][0], line[line.length - 1][1]) ) <= 14) { // save annotations - this.__endNewFeature(); + await this.__endNewFeature(); try { // custom event on stop this.raiseEvent('stop-drawing', {originalEvent: e}); @@ -720,7 +722,6 @@ } // anything happening? if (!(this.isDrawing) && !(this.isMoving)) return; - let point = new OpenSeadragon.Point(e.clientX, e.clientY); let img_point = this._viewer.viewport.windowToImageCoordinates(point); if ( @@ -920,7 +921,7 @@ * @private * stop drawing on the drawing canvas, when the mouse is up */ - stopDrawing: function(e) { + stopDrawing: async function(e) { // stop if the draw mode is pointToPoint if(this.drawMode == 'pointToPoint') { return; @@ -929,7 +930,7 @@ if ((this.isDrawing) || (this.isMoving)) { // add style and data to data collection // saving the stroke to all data - this.__endNewFeature(); + await this.__endNewFeature(); try { // custom event on stop this.raiseEvent('stop-drawing', {originalEvent: e}); @@ -1107,7 +1108,7 @@ // align points = this.__align(points); let pointsList; - if ($UI.AssistantViewer.__isEnableAssistant()) { + if ($UI.AssistantViewer?.__isEnableAssistant()) { const mlPoints = await this.__mlDraw(points); pointsList = mlPoints; } else { @@ -1433,7 +1434,6 @@ dist[i] = [Math.floor(dist[i].x * this.align_fx), Math.floor(dist[i].y * this.align_fy)]; } let drawMany; - console.log('mode: ',$UI.AssistantViewer.__getAssistantMode()); if ($UI.AssistantViewer.__getAssistantMode() === 'annotateOneByDraw') { drawMany = false; } else if ($UI.AssistantViewer.__getAssistantMode() === 'annotateManyByDraw') { From 991d169f6c3ae2a5254f6af905106bc8c434b9b3 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 24 Jul 2023 14:56:54 +0700 Subject: [PATCH 15/46] fix undo functions with annotation assistant --- .../openseadragon-canvas-draw-overlay.js | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index f7a0bccaa..dcf3e3fb2 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -1213,21 +1213,26 @@ * redraw the previous mark if it exist. */ undo: function() { - if (this._path_index > 0) + if (this._path_index > this.lastPointsListLength - 1) // redraw path { this.drawOnCanvas( this._display_ctx_, function() { - this.drawMode !== 'grid' ? - DrawHelper.draw( - this._display_ctx_, - this._draws_data_.slice(0, --this._path_index), - ) : - DrawHelper.drawGrids( - this._display_ctx_, - this._draws_data_.slice(0, --this._path_index), - ); + this._path_index -= this.lastPointsListLength; + this.drawMode !== 'grid' ? + DrawHelper.draw( + this._display_ctx_, + this._draws_data_.slice(0, this._path_index), + ) : + DrawHelper.drawGrids( + this._display_ctx_, + this._draws_data_.slice(0, this._path_index), + ); + this.lastPointsListLength = 1; + try { + this.currentOriginPath = JSON.parse(JSON.stringify(this._draws_data_[this._path_index-1])); + } catch (error) {} }.bind(this), ); } From 5bf920608d87bb13c39cc9705b2737d178a42446 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 24 Jul 2023 16:00:48 +0700 Subject: [PATCH 16/46] Fix disable assistant mode when assistant menu close --- apps/viewer/uicallbacks.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index 91462d694..0d4dee7cc 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -2048,6 +2048,7 @@ function presetLabelOff() { } function mlAsisstantOff() { + $UI.AssistantViewer.enableBtn.checked = false; $UI.AssistantSideMenu.close(); } From ddeaa54e6f0627c3a139c55bd20947b12fcce361 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 24 Jul 2023 16:18:03 +0700 Subject: [PATCH 17/46] Fix multiple points annotation points --- core/extension/openseadragon-canvas-draw-overlay.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index dcf3e3fb2..7c55bb8db 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -1368,7 +1368,9 @@ this._hash_data = new Map(); for (var i = 0; i < this._path_index; i++) { var cur = this._draws_data_[i].geometry.coordinates[0]; - cur = mtool.populate(cur, ~~this.scaleWindowtoImage(5), ~~this.scaleWindowtoImage(1), 150); + if (this._draws_data_[i].geometry.type !== 'Point') { + cur = mtool.populate(cur, ~~this.scaleWindowtoImage(5), ~~this.scaleWindowtoImage(1), 150); + } for (var j = 0; j < cur.length; j++) { this._hash_data[mtool.hash({ x: cur[j][0], From 0624c55d696d61db5725ac3714a7bed0f38bd7af Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:08:36 +0100 Subject: [PATCH 18/46] DCM type: batchloader.js --- apps/batchloader/batchLoader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/batchloader/batchLoader.js b/apps/batchloader/batchLoader.js index 2e15cb803..a91b2d9c1 100644 --- a/apps/batchloader/batchLoader.js +++ b/apps/batchloader/batchLoader.js @@ -11,7 +11,7 @@ let finishUrl = '../../loader/upload/finish/'; let checkUrl = '../../loader/data/one/'; let chunkSize = 5*1024*1024; let finishUploadSuccess = false; -const allowedExtensions = ['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide']; +const allowedExtensions = ['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide', 'dcm']; // call on document ready $(document).ready(function() { From dd400dfda508022828d5c054553bbe8bd9e19abe Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:09:30 +0100 Subject: [PATCH 19/46] DCM type: batchloader.html --- apps/batchloader/batchloader.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/batchloader/batchloader.html b/apps/batchloader/batchloader.html index d6d7d4660..7f99d28ff 100644 --- a/apps/batchloader/batchloader.html +++ b/apps/batchloader/batchloader.html @@ -70,7 +70,7 @@ type="file" class="custom-file-input" id="filesInput" - accept=".svs, .tif, .tiff, .vms, .vmu, .ndpi, .scn, .mrxs, .bif, .svslide" + accept=".svs, .tif, .tiff, .vms, .vmu, .ndpi, .scn, .mrxs, .bif, .svslide, .dcm" multiple required /> From c9d201c9015bc306920e873d4c72719fe261392a Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Wed, 26 Jul 2023 20:10:11 +0100 Subject: [PATCH 20/46] DCM type: table.js --- apps/table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/table.js b/apps/table.js index e3fe07fc9..7a0907bf3 100644 --- a/apps/table.js +++ b/apps/table.js @@ -13,7 +13,7 @@ function sanitize(string) { } var existingSlideNames = []; var permissions; -const allowedExtensions = ['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide', 'jpg', 'png']; +const allowedExtensions = ['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide', 'jpg', 'png', 'dcm']; function validateForm(callback) { let slide = document.getElementById('slidename0'); // Check if input element is rendered or not From 84c2f92989c664683a60691bdad147a42090435f Mon Sep 17 00:00:00 2001 From: Ryan Birmingham Date: Tue, 1 Aug 2023 16:58:36 -0400 Subject: [PATCH 21/46] don't wait for thumbnail to return before allowing post --- apps/loader/loader.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/loader/loader.js b/apps/loader/loader.js index 39ca66153..47f96c4ba 100644 --- a/apps/loader/loader.js +++ b/apps/loader/loader.js @@ -59,17 +59,8 @@ function changeStatus(step, text, reset=true) { } } if (step == 'CHECK') { - // During check, thumbnail needs to be fetched & added to the table - // In this case, text[col[col.length - 1]] is the filename - fetch(thumbUrl + text[col[col.length - 1]], {credentials: 'same-origin'}).then( - (response) => response.json(), // if the response is a JSON object - ).then((x)=>{ - var tabCell = tr.cells[tr.cells.length-1]; - tabCell.innerHTML = ''; - const img = new Image(); - img.src = x.slide; - tabCell.appendChild(img); - if (text['location']) { + // show post button + if (text['location']) { // indicating successful check checkSuccess = true; if (finishUploadSuccess === true) { @@ -81,8 +72,18 @@ function changeStatus(step, text, reset=true) { checkSuccess = false; $('#post_btn').hide(); } - }); - } + // fetch thumbnail and add to table as we can + // In this case, text[col[col.length - 1]] is the filename + fetch(thumbUrl + text[col[col.length - 1]], {credentials: 'same-origin'}).then( + (response) => response.json(), // if the response is a JSON object + ).then((x)=>{ + var tabCell = tr.cells[tr.cells.length-1]; + tabCell.innerHTML = ''; + const img = new Image(); + img.src = x.slide; + tabCell.appendChild(img); + }); + } } var divContainer = document.getElementById('json_table'); From ef2f40959d46e1a7505ccb21372257eeab20141f Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Fri, 4 Aug 2023 14:52:03 +0900 Subject: [PATCH 22/46] Change UI of annotation assistant UI and openning method --- apps/viewer/init.js | 16 +++++++++++++--- components/sidemenu/sidemenu.js | 24 ++++++++++-------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index 0cd1e7c40..271137f62 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -779,15 +779,17 @@ async function initUIcomponents() { /* --- machine learning Assistant --- */ $UI.AssistantSideMenu = new SideMenu({ id: 'ml_assistant_layers', - width: 240, + width: 'unset', contentPadding: 5, position: 'right', - height: '50vh', - top: '30px' + height: 'unset', + top: '40px', + borderRadius: '10px', }); var AssistantTitle = document.createElement('div'); AssistantTitle.classList.add('item_head'); + AssistantTitle.style.margin = 0; AssistantTitle.textContent = 'Annotation Assistant'; $UI.AssistantSideMenu.addContent(AssistantTitle); @@ -835,6 +837,14 @@ async function initUIcomponents() { }); $UI.AssistantViewer.elt.parentNode.removeChild($UI.AssistantViewer.elt); $UI.AssistantSideMenu.addContent($UI.AssistantViewer.elt); + $UI.AssistantViewer.elt.style.display = 'none'; + AssistantTitle.addEventListener('click', () => { + if ($UI.AssistantViewer.elt.style.display === 'none') { + $UI.AssistantViewer.elt.style.display = ''; + } else { + $UI.AssistantViewer.elt.style.display = 'none'; + } + }) // create UI and set data $UI.layersViewer = createLayerViewer( diff --git a/components/sidemenu/sidemenu.js b/components/sidemenu/sidemenu.js index 38e9253a7..07358d27a 100644 --- a/components/sidemenu/sidemenu.js +++ b/components/sidemenu/sidemenu.js @@ -91,6 +91,10 @@ SideMenu.prototype.__refresh = function() { this.elt.style.height = '100vh'; } + if (this.setting.borderRadius) { + this.elt.style.borderRadius = this.setting.borderRadius; + } + if (this.setting.top) { this.elt.style.top = this.setting.top; } else { @@ -109,19 +113,7 @@ SideMenu.prototype.__refresh = function() { this._close_handler = document.createElement('div'); this._close_handler.classList.add('close'); - if (this.setting.position === 'right') { - const icon1 = document.createElement('i'); - icon1.classList.add('material-icons'); - icon1.classList.add('md-24'); - icon1.classList.add('right'); - icon1.textContent = 'chevron_right'; - - const icon2 = icon1.cloneNode(true); - - icon1.classList.add('fir'); - this._close_handler.appendChild(icon1); - this._close_handler.appendChild(icon2); - } else { + if (this.setting.position === 'left') { const icon1 = document.createElement('i'); icon1.classList.add('material-icons'); icon1.classList.add('md-24'); @@ -147,7 +139,11 @@ SideMenu.prototype.__refresh = function() { * open the side menu */ SideMenu.prototype.open = function() { - this.elt.style.width = this.setting.width+'px'; + if (this.setting.width === 'unset') { + this.elt.style.width = 'unset'; + } else { + this.elt.style.width = this.setting.width+'px'; + } if (this.setting.position === 'right') { this.elt.style.left = 'unset'; this.elt.style.right = 0; From 71250999295b9c72ca212afd6a3ec5eac07a2948 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Fri, 4 Aug 2023 14:57:38 +0900 Subject: [PATCH 23/46] Intergrate annotation assistant into brush preset label --- apps/viewer/init.js | 1 + apps/viewer/uicallbacks.js | 258 +++++++++--------- common/util.js | 44 +++ .../openseadragon-canvas-draw-overlay.js | 127 ++++++--- 4 files changed, 272 insertions(+), 158 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index 271137f62..e6b689bef 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -842,6 +842,7 @@ async function initUIcomponents() { if ($UI.AssistantViewer.elt.style.display === 'none') { $UI.AssistantViewer.elt.style.display = ''; } else { + // $UI.AssistantViewer.enableBtn.checked = false; $UI.AssistantViewer.elt.style.display = 'none'; } }) diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index 0d4dee7cc..1f1d86e18 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -318,6 +318,7 @@ function toolsOff() { break; case 'label': presetLabelOff(); + mlAsisstantOff(); break; case 'download_selection': downloadSelectionOff(); @@ -665,6 +666,7 @@ function mainMenuChange(data) { $UI.labelsSideMenu.open(); } else { presetLabelOff(); + mlAsisstantOff(); } } @@ -1976,6 +1978,7 @@ function drawLabel(e) { if (e.checked) { if ($CAMIC.status == 'label') { presetLabelOn.call(this, labels); + mlAsisstantOn(); return; } // turn off annotation @@ -1987,6 +1990,7 @@ function drawLabel(e) { // all tool has turn off clearInterval(checkAllToolsOff); presetLabelOn.call(this, labels); + mlAsisstantOn(); } }.bind(this), 100, @@ -1994,6 +1998,7 @@ function drawLabel(e) { } else { // off preset label presetLabelOff(); + mlAsisstantOff(); } } @@ -2049,6 +2054,7 @@ function presetLabelOff() { function mlAsisstantOff() { $UI.AssistantViewer.enableBtn.checked = false; + $UI.AssistantViewer.elt.style.display = 'none'; $UI.AssistantSideMenu.close(); } @@ -2057,7 +2063,6 @@ function mlAsisstantOn() { } function savePresetLabel() { - console.log('savePresetLabel'); if ($CAMIC.viewer.canvasDrawInstance._path_index === 0) { // toast $UI.message.addWarning('info'+ @@ -2069,136 +2074,137 @@ function savePresetLabel() { $UI.message.addWarning('No Label Selected. Please select One.', 4000); return; } - const execId = randomId(); - const labelId = data.id; - const labelName = data.type; - // const parent = data.type; - const noteData = { - id: execId, - labelId: labelId, - name: labelName, - notes: data.type, - }; - const feature = $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection() - .features[0]; - let annotJson; - if (feature.properties.size) { - // brush - const values = getGrids( - feature.geometry.coordinates[0], - feature.properties.size, - ); - const set = new Set(); - values.map((i) => i.toString()).forEach((v) => set.add(v)); - const points = Array.from(set).map((d) => d.split(',')); - annotJson = { - creator: getUserId(), - created_date: new Date(), - provenance: { - image: { - slide: $D.params.slideId, + const features = $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection().features + for (let feature of features) { + const execId = randomId(); + const labelId = data.id; + const labelName = data.type; + // const parent = data.type; + const noteData = { + id: execId, + labelId: labelId, + name: labelName, + notes: data.type, + }; + let annotJson; + if (feature.properties.size) { + // brush + const values = getGrids( + feature.geometry.coordinates[0], + feature.properties.size, + ); + const set = new Set(); + values.map((i) => i.toString()).forEach((v) => set.add(v)); + const points = Array.from(set).map((d) => d.split(',')); + annotJson = { + creator: getUserId(), + created_date: new Date(), + provenance: { + image: { + slide: $D.params.slideId, + }, + analysis: { + source: 'human', + execution_id: execId, // randomId + name: labelName, // labelName + labelId: labelId, + type: 'label', + isGrid: true, + }, }, - analysis: { - source: 'human', - execution_id: execId, // randomId - name: labelName, // labelName - labelId: labelId, - type: 'label', - isGrid: true, + properties: { + annotations: noteData, }, - }, - properties: { - annotations: noteData, - }, - geometries: convertGeometries(points, { - note: data.type, - size: feature.properties.size, - color: feature.properties.style.color, - }), - }; - } else { - // point / polygon / stringLine - annotJson = { - creator: getUserId(), - created_date: new Date(), - provenance: { - image: { - slide: $D.params.slideId, + geometries: convertGeometries(points, { + note: data.type, + size: feature.properties.size, + color: feature.properties.style.color, + }), + }; + } else { + // point / polygon / stringLine + annotJson = { + creator: getUserId(), + created_date: new Date(), + provenance: { + image: { + slide: $D.params.slideId, + }, + analysis: { + source: 'human', + execution_id: execId, // randomId + name: labelName, // labelName + labelId: labelId, + type: 'label', + }, }, - analysis: { - source: 'human', - execution_id: execId, // randomId - name: labelName, // labelName - labelId: labelId, - type: 'label', + properties: { + annotations: noteData, }, - }, - properties: { - annotations: noteData, - }, - geometries: ImageFeaturesToVieweportFeatures( - $CAMIC.viewer, - $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection(), - ), - }; - } - - $CAMIC.store - .addMark(annotJson) - .then((data) => { - // server error - if (data.error) { - $UI.message.addError(`${data.text}:${data.url}`); - Loading.close(); - return; - } - - // no data added - if (data.count < 1) { + geometries: ImageFeaturesToVieweportFeatures( + $CAMIC.viewer, + $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection(), + ), + }; + } + + $CAMIC.store + .addMark(annotJson) + .then((data) => { + // server error + if (data.error) { + $UI.message.addError(`${data.text}:${data.url}`); + Loading.close(); + return; + } + + // no data added + if (data.count < 1) { + Loading.close(); + $UI.message.addWarning(`Annotation Save Failed`); + return; + } + const __data = data.ops[0]; + // create layer data + const newItem = { + id: execId, + name: noteData.name, + typeId: 'human', + typeName: 'human', + creator: getUserId(), + shape: annotJson.geometries.features[0].geometry.type, + isGrid: annotJson.provenance.analysis.isGrid? true: false, + label: { + id: annotJson.provenance.analysis.labelId, + name: annotJson.provenance.analysis.name, + }, + data: null, + }; + $D.humanlayers.push(newItem); + $UI.layersViewer.addHumanItem(newItem, 'human', labelId); + $UI.layersViewerMinor.addHumanItem( + newItem, + 'human', + labelId, + $minorCAMIC && $minorCAMIC.viewer ? true : false, + ); + + __data._id = {$oid: __data._id}; + addAnnotation( + execId, + __data, + 'human', + labelId, + ); + }) + .catch((e) => { Loading.close(); - $UI.message.addWarning(`Annotation Save Failed`); - return; - } - const __data = data.ops[0]; - // create layer data - const newItem = { - id: execId, - name: noteData.name, - typeId: 'human', - typeName: 'human', - creator: getUserId(), - shape: annotJson.geometries.features[0].geometry.type, - isGrid: annotJson.provenance.analysis.isGrid? true: false, - label: { - id: annotJson.provenance.analysis.labelId, - name: annotJson.provenance.analysis.name, - }, - data: null, - }; - $D.humanlayers.push(newItem); - $UI.layersViewer.addHumanItem(newItem, 'human', labelId); - $UI.layersViewerMinor.addHumanItem( - newItem, - 'human', - labelId, - $minorCAMIC && $minorCAMIC.viewer ? true : false, - ); - - __data._id = {$oid: __data._id}; - addAnnotation( - execId, - __data, - 'human', - labelId, - ); - }) - .catch((e) => { - Loading.close(); - console.log('save failed', e); - }) - .finally(() => { - $UI.message.addSmall(`Added The '${noteData.name}' Annotation.`); - }); + console.log('save failed', e); + }) + .finally(() => { + $UI.message.addSmall(`Added The '${noteData.name}' Annotation.`); + }); + } } function addAnnotation(id, data, type, parent) { diff --git a/common/util.js b/common/util.js index 02ec37ec6..0f6620847 100644 --- a/common/util.js +++ b/common/util.js @@ -592,6 +592,50 @@ function getGrids(points, size) { return grids; } +function areaCircumferenceToGrids(points, size) { + const grids = []; + // get boundary box of the points + const bound = getBounds(points); + const minX = bound[0][0]; + const maxX = bound[2][0]; + const minY = bound[0][1]; + const maxY = bound[2][1]; + // get all centers inside the boundary box + const topLeftCenter = [Math.ceil((minX - size[0] / 2) / size[0]) * size[0] + size[0] / 2, Math.ceil((minY - size[1] / 2) / size[1]) * size[1] + size[1] / 2]; + for (let centerX = topLeftCenter[0]; centerX < maxX; centerX += size[0]) { + for (let centerY = topLeftCenter[1]; centerY < maxY; centerY += size[1]) { + if (isPointInsidePolygon([centerX, centerY], points)) { + grids.push(getTopLeft([centerX, centerY], size)) + } + } + } + return grids; +} + +function isPointInsidePolygon(point, polygon) { + const [x, y] = point; + const n = polygon.length; + let inside = false; + + for (let i = 0; i < n; i++) { + const [x1, y1] = polygon[i]; + const [x2, y2] = polygon[(i + 1) % n]; + + if (y === y1 && y1 === y2 && (x1 <= x && x <= x2 || x2 <= x && x <= x1)) { + return true; + } + + if ((y1 < y && y < y2 || y2 < y && y < y1) && x < Math.max(x1, x2)) { + const intersectionX = (y - y1) * (x2 - x1) / (y2 - y1) + x1; + if (x < intersectionX) { + inside = !inside; + } + } + } + + return inside; +} + function getTopLeft(point, size) { return [ Math.floor(point[0] / size[0]) * size[0], diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index 7c55bb8db..f89625a80 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -807,26 +807,76 @@ ); break; case 'grid': - // draw line - this._last = [img_point.x, img_point.y]; - // store current point - this._current_path_.geometry.coordinates[0].push(this._last.slice()); - const grids = getGrids( - this._current_path_.geometry.coordinates[0], - this._current_path_.properties.size, - ); - this._draw_ctx_.fillStyle = hexToRgbA(this.style.color, 0.5); - this.drawOnCanvas( - this._draw_ctx_, - function() { - DrawHelper.drawMultiGrid( - this._draw_ctx_, - grids, - this._current_path_.properties.size, - ); - // DrawHelper.drawGrid(this._draw_ctx_,this._current_path_.geometry.coordinates[0]); - }.bind(this), - ); + // draw line + if ($UI.AssistantViewer?.__isEnableAssistant()) { + // draw line + this._last = [img_point.x, img_point.y]; + // store current point + this._current_path_.geometry.coordinates[0].push(this._last.slice()); + this.drawOnCanvas( + this._draw_ctx_, + function() { + // draw circle + const sx = this._current_path_.geometry.coordinates[0][0][0]; + const sy = this._current_path_.geometry.coordinates[0][0][1]; + let start = new OpenSeadragon.Point(sx, sy); + start = this._viewer.viewport.imageToWindowCoordinates(start); + const ex = this._current_path_.geometry.coordinates[0][ + this._current_path_.geometry.coordinates[0].length - 1 + ][0]; + const ey = this._current_path_.geometry.coordinates[0][ + this._current_path_.geometry.coordinates[0].length - 1 + ][1]; + let end = new OpenSeadragon.Point(ex, ey); + end = this._viewer.viewport.imageToWindowCoordinates(end); + const dx = Math.round(Math.abs(start.x - end.x)); + const dy = Math.round(Math.abs(start.y - end.y)); + const distance = Math.sqrt(dx * dx + dy * dy); + this._draw_ctx_.strokeStyle = distance > 14 ? 'red' : 'blue'; + // start point + DrawHelper.drawCircle( + this._draw_ctx_, + sx, + sy, + this.style.lineWidth * 3, + ); + // end point + DrawHelper.drawCircle( + this._draw_ctx_, + ex, + ey, + this.style.lineWidth * 3, + ); + + DrawHelper.setStyle(this._draw_ctx_, this.style); + // draw circle + DrawHelper.drawMultiline( + this._draw_ctx_, + this._current_path_.geometry.coordinates[0], + ); + }.bind(this), + ); + } else { + this._last = [img_point.x, img_point.y]; + // store current point + this._current_path_.geometry.coordinates[0].push(this._last.slice()); + const grids = getGrids( + this._current_path_.geometry.coordinates[0], + this._current_path_.properties.size, + ); + this._draw_ctx_.fillStyle = hexToRgbA(this.style.color, 0.5); + this.drawOnCanvas( + this._draw_ctx_, + function() { + DrawHelper.drawMultiGrid( + this._draw_ctx_, + grids, + this._current_path_.properties.size, + ); + // DrawHelper.drawGrid(this._draw_ctx_,this._current_path_.geometry.coordinates[0]); + }.bind(this), + ); + } // this.drawOnCanvas(this._draw_ctx_,function(){ // DrawHelper.drawMultiline(this._draw_ctx_,this._current_path_.geometry.coordinates[0]); // }.bind(this)); @@ -1180,7 +1230,7 @@ this._current_path_.geometry.coordinates[0] = drawPoints; // create bounds this._current_path_.bound.coordinates[0] = getBounds( - this._current_path_.geometry.coordinates[0], + this._current_path_.geometry.coordinates[0] ); if (this._path_index < this._draws_data_.length) { @@ -1195,16 +1245,16 @@ this.drawOnCanvas( this._display_ctx_, function() { - // this.drawMode !== "grid" - // ? - DrawHelper.draw( - this._display_ctx_, - this._draws_data_.slice(0, this._path_index), - ); - // : DrawHelper.drawGrids( - // this._display_ctx_, - // this._draws_data_.slice(0, this._path_index) - // ); + this.drawMode !== "grid" + ? + DrawHelper.draw( + this._display_ctx_, + this._draws_data_.slice(0, this._path_index), + ) + : DrawHelper.drawGrids( + this._display_ctx_, + this._draws_data_.slice(0, this._path_index) + ); }.bind(this), ); }, @@ -1447,7 +1497,7 @@ drawMany = true; } dists = await mltools.applyDraw(dist, threshold, radius, overlap, scaleMethod, drawMany); - return dists.map((dist) => { + var distAligns = dists.map((dist) => { const distAlign = []; for (i = 0; i < dist.length; i++) { dist[i] = new OpenSeadragon.Point(dist[i][0] / this.align_fx, dist[i][1] / this.align_fy); @@ -1458,6 +1508,19 @@ } return distAlign; }); + + // Process for each type of annotation mode + switch (this.drawMode) { + case 'grid': + distAligns = distAligns.map((dist) => { + const gridPoints = areaCircumferenceToGrids(dist, this._current_path_.properties.size); + return gridPoints; + }); + break; + default: + break; + } + return distAligns; } }; From cdeac8c71249c007c50f962136d89c9c1afe6bac Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Wed, 9 Aug 2023 10:54:46 +0700 Subject: [PATCH 24/46] optimize find contour algorithm --- components/ml-assistant/ml-tool.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/ml-assistant/ml-tool.js b/components/ml-assistant/ml-tool.js index 6638e6e8f..03ef8ba2b 100644 --- a/components/ml-assistant/ml-tool.js +++ b/components/ml-assistant/ml-tool.js @@ -259,8 +259,9 @@ class mlTools { expandContour(contour, width, height, size, iter) { const mask = new cv.Mat.zeros(height, width, cv.CV_8UC1); const point = new cv.Point(0, 0); - for (let i = 0; i < width; i++) { - for (let j = 0; j < height; j++) { + const bound = cv.boundingRect(contour); + for (let i = bound.x; i < bound.x + bound.width; i++) { + for (let j = bound.y; j < bound.y + bound.height; j++) { point.x = i; point.y = j; if (cv.pointPolygonTest(contour, point, false) >= 0) { From b46a09531b6c86ca41d960c1a44b49f53f84d97f Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Sun, 13 Aug 2023 17:08:15 +0700 Subject: [PATCH 25/46] fix non array and inaccurate grids return from ml assistant --- common/util.js | 4 +++- core/extension/openseadragon-canvas-draw-overlay.js | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/util.js b/common/util.js index 0f6620847..1b1bcd05f 100644 --- a/common/util.js +++ b/common/util.js @@ -613,7 +613,9 @@ function areaCircumferenceToGrids(points, size) { } function isPointInsidePolygon(point, polygon) { - const [x, y] = point; + let [x, y] = point; + x += 0.01; + y += 0.01; const n = polygon.length; let inside = false; diff --git a/core/extension/openseadragon-canvas-draw-overlay.js b/core/extension/openseadragon-canvas-draw-overlay.js index f89625a80..1bb203e82 100644 --- a/core/extension/openseadragon-canvas-draw-overlay.js +++ b/core/extension/openseadragon-canvas-draw-overlay.js @@ -1515,6 +1515,8 @@ distAligns = distAligns.map((dist) => { const gridPoints = areaCircumferenceToGrids(dist, this._current_path_.properties.size); return gridPoints; + }).filter((dist) => { + return dist.length > 0; }); break; default: From 94dbb20db454a9f14516c1e8c797cde59235fe1e Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Mon, 14 Aug 2023 16:15:07 +0700 Subject: [PATCH 26/46] change edit mouse up handler of annotation edit tool --- .../openseadragon-overlays-manage.js | 62 +++++++------------ 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/core/extension/openseadragon-overlays-manage.js b/core/extension/openseadragon-overlays-manage.js index 9bdc5b39b..496a803fc 100644 --- a/core/extension/openseadragon-overlays-manage.js +++ b/core/extension/openseadragon-overlays-manage.js @@ -241,7 +241,14 @@ * @param {Event} e the event */ pathClick:function(e){ - if(this.highlightLayer&&this.highlightLayer.clickable) { + if (this.onEdit) { + this.onEditPointMouseUp(e); + this.editPointStyle = this.highlightStyle || this.editPointStyle; + this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this.editPathData, this.editPointStyle]); + this.updateView(); + return; + } + else if(this.highlightLayer&&this.highlightLayer.clickable) { if (this.currentEditIndex !== this.currentHighlightIndex) { this._viewer.raiseEvent('canvas-lay-click',{position:{x:e.clientX, y:e.clientY}, data:this.highlightLayer?this.highlightLayer.data:null}); if (this.currentEditIndex) { @@ -252,7 +259,6 @@ this._viewer.setMouseNavEnabled(false); this._div.addEventListener('mousemove', this.onEditPointMouseMove.bind(this)); this._div.addEventListener('mouseout', this.onEditPointMouseUp.bind(this)); - this._div.addEventListener('mouseup', this.onEditPointMouseUp.bind(this)); this._div.addEventListener('mousedown', this.onEditPointMouseDown.bind(this)); } else { try { @@ -262,46 +268,23 @@ this._viewer.setMouseNavEnabled(true); this._div.removeEventListener('mousemove', this.onEditPointMouseMove.bind(this)); this._div.removeEventListener('mouseout', this.onEditPointMouseUp.bind(this)); - this._div.removeEventListener('mouseup', this.onEditPointMouseUp.bind(this)); this._div.removeEventListener('mousedown', this.onEditPointMouseDown.bind(this)); } this.editPathData = this.highlightPathData; - this.editPath = this.highlightPath; this.editStyle = this.highlightStyle; - const editPointStyle = { - color: "#000000", - isFill: true, - lineCap: "round", - lineJoin: "round" - }; - this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this._div, this.editPathData, this.editPath, editPointStyle]); + this.editPointStyle = this.highlightStyle || this.editPointStyle; + this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this.editPathData, this.editPointStyle]); this.updateView(); }, - drawEditPoints: function(ctx, div, pathData, path, style) { - if (!pathData) return; - if(style.isFill ==undefined || style.isFill){ - const imagingHelper = this._viewer.imagingHelper; - const lineWidth = (imagingHelper.physicalToDataX(1) - imagingHelper.physicalToDataX(0))>> 0; - ctx.lineWidth = lineWidth; - ctx.fillStyle = hexToRgbA(this.editStyle.color, 0.4); - ctx.strokeStyle = style.color; - path.stroke(ctx); - path.fill(ctx); - }else{ - const imagingHelper = this._viewer.imagingHelper; - const lineWidth = (imagingHelper.physicalToDataX(1) - imagingHelper.physicalToDataX(0))>> 0; - ctx.lineWidth = lineWidth; - ctx.fillStyle = hexToRgbA(this.editStyle.color, 0.4); - ctx.strokeStyle = style.color; - path.stroke(ctx); - } + drawEditPoints: function(ctx, pathData, style) { + if (!pathData) return; this.editPointPathList = []; pathData = pathData.geometry.coordinates; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; - ctx.fillStyle = hexToRgbA(style.color, 1); + ctx.fillStyle = hexToRgbA(style.color, 0.7); ctx.strokeStyle = style.color; ctx.lineWidth = style.lineWidth; @@ -321,7 +304,7 @@ pointPath.arc( point[0], point[1], - ctx.radius * 2, 0, 2 * Math.PI + ctx.radius * 1.5, 0, 2 * Math.PI ); pointPath.closePath(); pointPath.strokeAndFill(ctx); @@ -334,19 +317,20 @@ DrawHelper.clearCanvas(this._edit_tool_hover_); if (!this.editPathData) return; if (!this.editPointPathList) return; - const editPointStyle = { - color: "#000000", - isFill: true, - lineCap: "round", - lineJoin: "round" - }; + // const editPointStyle = { + // color: "#000000", + // isFill: true, + // lineCap: "round", + // lineJoin: "round" + // }; + this.editPointStyle = this.highlightStyle || this.editPointStyle; const point = new OpenSeadragon.Point(e.clientX, e.clientY); const img_point = this._viewer.viewport.windowToImageCoordinates(point); for(let i = 0;i Date: Mon, 14 Aug 2023 22:32:59 +0700 Subject: [PATCH 27/46] Remove and add annotation point for annotation edit function --- common/util.js | 51 +++++++++ .../openseadragon-overlays-manage.js | 105 ++++++++++++++++-- 2 files changed, 149 insertions(+), 7 deletions(-) diff --git a/common/util.js b/common/util.js index 1b1bcd05f..30cc6473a 100644 --- a/common/util.js +++ b/common/util.js @@ -612,6 +612,57 @@ function areaCircumferenceToGrids(points, size) { return grids; } +function distance(x1, y1, x2, y2) { + const dx = x2 - x1; + const dy = y2 - y1; + return Math.sqrt(dx * dx + dy * dy); +} + +// Find the closest point on a line segment to a given point +function closestPointOnLineSegment(px, py, x1, y1, x2, y2) { + const dx = x2 - x1; + const dy = y2 - y1; + const t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy); + + if (t < 0) { + return { x: x1, y: y1 }; + } else if (t > 1) { + return { x: x2, y: y2 }; + } else { + return { x: x1 + t * dx, y: y1 + t * dy }; + } +} + +function closestPointOnPolygon(polygon, px, py) { + let closestDistance = Infinity; + let closestIndex = null; + + // Find the closest point on each edge + for (let i = 0; i < polygon.length; i++) { + const nextIndex = (i + 1) % polygon.length; + const edgeStart = polygon[i]; + const edgeEnd = polygon[nextIndex]; + const closest = closestPointOnLineSegment(px, py, edgeStart[0], edgeStart[1], edgeEnd[0], edgeEnd[1]); + const d = distance(px, py, closest.x, closest.y); + + if (d < closestDistance) { + closestDistance = d; + closestIndex = i; + } + } + + return closestIndex; +} + +function pointInPolygonVertex(polygon, point) { + for (let i = 0; i < polygon.length; i++) { + if (polygon[i][0] === point[0] && polygon[i][1] === point[1]) { + return i; + } + } + return false; +} + function isPointInsidePolygon(point, polygon) { let [x, y] = point; x += 0.01; diff --git a/core/extension/openseadragon-overlays-manage.js b/core/extension/openseadragon-overlays-manage.js index 496a803fc..6116e7d9b 100644 --- a/core/extension/openseadragon-overlays-manage.js +++ b/core/extension/openseadragon-overlays-manage.js @@ -48,6 +48,7 @@ zooming:this._zooming.bind(this), panning:this._panning.bind(this), drawing:this._drawing.bind(this), + contextmenu: this.rightClick.bind(this), } // -- create container div, and hover, display canvas -- // this._containerWidth = 0; @@ -109,7 +110,7 @@ this._div.addEventListener('mousemove', this.events.highlight); this._div.addEventListener('click', this.events.click); - + this._div.addEventListener('contextmenu', this.events.contextmenu); } @@ -158,6 +159,90 @@ } this.updateView(); }, + + rightClick: function(e) { + e.preventDefault(); + if (!this.editPathData) { + return; + } + DrawHelper.clearCanvas(this._edit_tool_); + const point = new OpenSeadragon.Point(e.clientX, e.clientY); + const img_point = this._viewer.viewport.windowToImageCoordinates(point); + // if hover on an edit point then remove the point + for (let i = 0; i < this.editPointPathList.length; i++) { + if (this.editPointPathList[i].contains(img_point.x, img_point.y)) { + this.editPointPathList = []; + if (this.editPathData.geometry.type === 'Polygon') { + // handle last point data + if (i === 0 || i === this.editPathData.geometry.coordinates[0].length - 1) { + this.editPathData.geometry.coordinates[0].splice(0,1); + this.editPathData.geometry.coordinates[0][this.editPathData.geometry.coordinates[0].length - 1] = this.editPathData.geometry.coordinates[0][0]; + } else { + this.editPathData.geometry.coordinates[0].splice(i,1); + } + this.editPathData.geometry.coordinates[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + }) + } else if (this.editPathData.geometry.type === 'LineString') { + // brush + this.editPathData.geometry.coordinates[0].splice(i,1); + this.editPathData.geometry.coordinates[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + }) + } + + // DrawHelper.draw(this._edit_tool_ctx_, [this.editPathData]); + this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this.editPathData, this.editPointStyle]); + this.updateView(); + this._viewer.raiseEvent('annot-edit-save',{id: this.overlays[this.currentEditIndex].data._id.$oid, slide: this.overlays[this.currentEditIndex].data.provenance.image.slide ,data:this.overlays[this.currentEditIndex].data}); + return; + } + } + if (this.editPathData.geometry.type === 'LineString') { + const size = this.editPathData.properties.size; + const topleft = [Math.floor(img_point.x/size[0])*size[0], Math.floor(img_point.y/size[1])*size[1]]; + const index = pointInPolygonVertex(this.editPathData.geometry.coordinates[0], topleft); + if (index === false) { + this.editPathData.geometry.coordinates[0].push(topleft); + } else { + this.editPathData.geometry.coordinates[0].splice(index, 1); + } + } else if (this.editPathData.geometry.type === 'Polygon') { + const index = closestPointOnPolygon(this.editPathData.geometry.coordinates[0], img_point.x, img_point.y); + this.editPathData.geometry.coordinates[0].splice(index + 1,0,[img_point.x, img_point.y]); + this.editPathData.geometry.coordinates[0].map((point) => { + const pointPath = new Path(); + pointPath.arc( + point[0], + point[1], + this._edit_tool_ctx_.radius * 2, 0, 2 * Math.PI + ); + pointPath.closePath(); + pointPath.strokeAndFill(this._edit_tool_ctx_); + this.editPointPathList.push(pointPath); + }) + } + this.drawOnCanvas(this.drawEditPoints, [this._edit_tool_ctx_, this.editPathData, this.editPointStyle]); + this.updateView(); + this._viewer.raiseEvent('annot-edit-save',{id: this.overlays[this.currentEditIndex].data._id.$oid, slide: this.overlays[this.currentEditIndex].data.provenance.image.slide ,data:this.overlays[this.currentEditIndex].data}); + // Get position + }, /** * @private * highlight the path if cursor on the path @@ -317,12 +402,6 @@ DrawHelper.clearCanvas(this._edit_tool_hover_); if (!this.editPathData) return; if (!this.editPointPathList) return; - // const editPointStyle = { - // color: "#000000", - // isFill: true, - // lineCap: "round", - // lineJoin: "round" - // }; this.editPointStyle = this.highlightStyle || this.editPointStyle; const point = new OpenSeadragon.Point(e.clientX, e.clientY); const img_point = this._viewer.viewport.windowToImageCoordinates(point); @@ -355,6 +434,18 @@ }, onEditPointMouseDown: function(e) { + let isRight; + e = e || window.event; + if ('which' in e) + // Gecko (Firefox), WebKit (Safari/Chrome) & Opera + { + isRight = e.which == 3; + } else if ('button' in e) + // IE, Opera + { + isRight = e.button == 2; + } + if (e.ctrlKey || isRight) return; const point = new OpenSeadragon.Point(e.clientX, e.clientY); const img_point = this._viewer.viewport.windowToImageCoordinates(point); for (let i = 0; i < this.editPointPathList.length; i++) { From 4d27c00325dd374f958260034138f0ce147b4449 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Thu, 17 Aug 2023 11:13:32 +0700 Subject: [PATCH 28/46] Disable undo when open mlassistant on presetlabel --- apps/viewer/uicallbacks.js | 11 ++++++++--- components/ml-assistant/ml-assistant.js | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index 1f1d86e18..bfb37b38b 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -1978,7 +1978,7 @@ function drawLabel(e) { if (e.checked) { if ($CAMIC.status == 'label') { presetLabelOn.call(this, labels); - mlAsisstantOn(); + mlAsisstantOn(false); return; } // turn off annotation @@ -1990,7 +1990,7 @@ function drawLabel(e) { // all tool has turn off clearInterval(checkAllToolsOff); presetLabelOn.call(this, labels); - mlAsisstantOn(); + mlAsisstantOn(false); } }.bind(this), 100, @@ -2058,7 +2058,12 @@ function mlAsisstantOff() { $UI.AssistantSideMenu.close(); } -function mlAsisstantOn() { +function mlAsisstantOn(enableUndo = true) { + if (!enableUndo) { + $UI.AssistantViewer.disableUndo(); + } else { + $UI.AssistantViewer.enableUndo(); + } $UI.AssistantSideMenu.open(); } diff --git a/components/ml-assistant/ml-assistant.js b/components/ml-assistant/ml-assistant.js index 10b3562fd..bdeb14d94 100644 --- a/components/ml-assistant/ml-assistant.js +++ b/components/ml-assistant/ml-assistant.js @@ -176,6 +176,16 @@ Assistant.prototype.__refreshUI = async function() { this.__assignEventListener(); } +Assistant.prototype.disableUndo = function() { + this.undoBtn.style.backgroundColor = '#9c9c9cb0'; + this.undoBtn.style.color = '#0088ff'; +} + +Assistant.prototype.enableUndo = function() { + this.undoBtn.style.backgroundColor = '#ffffff'; + this.undoBtn.style.color = '#365f9c'; +} + Assistant.prototype.__assignEventListener = function() { // Open add model modal event this.addBtn.addEventListener('click', (event) => { From 91a37bfed32ee86d86b6d23afc05eb6c0fd242d1 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Thu, 17 Aug 2023 12:16:08 +0700 Subject: [PATCH 29/46] clean console.log --- apps/segment/segment.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/segment/segment.js b/apps/segment/segment.js index a99410095..e164cbb06 100644 --- a/apps/segment/segment.js +++ b/apps/segment/segment.js @@ -883,11 +883,9 @@ async function segmentModel(key) { // dummy.getContext('2d').drawImage(img, dx, dy);b const imgData = fullResCvs.getContext('2d').getImageData(0, 0, fullResCvs.width, fullResCvs.height); - console.log('imgData: ', imgData); let val; tf.tidy(()=>{ const img = tf.browser.fromPixels(imgData).toFloat(); - console.log('img: ', img); let img2; if (inputChannels == 1) { img2 = tf.image.resizeBilinear(img, [imageSize, imageSize]).mean(2); @@ -920,7 +918,6 @@ async function segmentModel(key) { normalized = img2.sub(mean).div(std); } const batched = normalized.reshape([1, imageSize, imageSize, inputChannels]); - console.log('batched: ', batched); let values = model.predict(batched).dataSync(); values = Array.from(values); // scale values From bf9117435f01aa3b3c4033afe95a4329ea3d85a3 Mon Sep 17 00:00:00 2001 From: Do Tien Dung Date: Thu, 17 Aug 2023 13:44:52 +0700 Subject: [PATCH 30/46] Fix preset label saving multiple annotation bugs --- apps/viewer/uicallbacks.js | 287 ++++++++++++++++++++----------------- 1 file changed, 159 insertions(+), 128 deletions(-) diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index bfb37b38b..bc76206bd 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -999,7 +999,6 @@ function editAnnoCallback(id, slide, annotJson) { }) .catch((e) => { Loading.close(); - console.log('save failed', e); }) .finally(() => {}); } @@ -1895,7 +1894,6 @@ function createHeatMapList(list) { async function addPresetLabelsHandler(label) { - console.log('label: ', label); const rs = await $CAMIC.store.addPresetLabels(label).then((d)=>d.result); if (rs.ok&&rs.nModified > 0) { @@ -1969,7 +1967,6 @@ function selectedPresetLabelsHandler(label) { } function drawLabel(e) { - console.log('run drawLabel'); if (!$CAMIC.viewer.canvasDrawInstance) { alert('Draw Doesn\'t Initialize'); return; @@ -2079,137 +2076,171 @@ function savePresetLabel() { $UI.message.addWarning('No Label Selected. Please select One.', 4000); return; } - const features = $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection().features - for (let feature of features) { - const execId = randomId(); - const labelId = data.id; - const labelName = data.type; - // const parent = data.type; - const noteData = { - id: execId, - labelId: labelId, - name: labelName, - notes: data.type, + const features = $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection().features; + const execId = randomId(); + const labelId = data.id; + const labelName = data.type; + // const parent = data.type; + const noteData = { + id: execId, + labelId: labelId, + name: labelName, + notes: data.type, + }; + let annotJson; + if (Array.isArray(features) && features[0].properties.size) { + // many brush + const values = features.reduce((p, f) => { + return p.concat(getGrids( + f.geometry.coordinates[0], + f.properties.size, + )); + },[]); + const set = new Set(); + values.map((i) => i.toString()).forEach((v) => set.add(v)); + const points = Array.from(set).map((d) => d.split(',')); + annotJson = { + creator: getUserId(), + created_date: new Date(), + provenance: { + image: { + slide: $D.params.slideId, + }, + analysis: { + source: 'human', + execution_id: execId, // randomId + name: labelName, // labelName + labelId: labelId, + type: 'label', + isGrid: true, + }, + }, + properties: { + annotations: noteData, + }, + geometries: convertGeometries(points, { + note: data.type, + size: features[0].properties.size, + color: features[0].properties.style.color, + }), }; - let annotJson; - if (feature.properties.size) { - // brush - const values = getGrids( - feature.geometry.coordinates[0], - feature.properties.size, - ); - const set = new Set(); - values.map((i) => i.toString()).forEach((v) => set.add(v)); - const points = Array.from(set).map((d) => d.split(',')); - annotJson = { - creator: getUserId(), - created_date: new Date(), - provenance: { - image: { - slide: $D.params.slideId, - }, - analysis: { - source: 'human', - execution_id: execId, // randomId - name: labelName, // labelName - labelId: labelId, - type: 'label', - isGrid: true, - }, + } else if (!Array.isArray(features) && features.properties.size) { + // brush + const values = getGrids( + features.geometry.coordinates[0], + features.properties.size, + ); + const set = new Set(); + values.map((i) => i.toString()).forEach((v) => set.add(v)); + const points = Array.from(set).map((d) => d.split(',')); + annotJson = { + creator: getUserId(), + created_date: new Date(), + provenance: { + image: { + slide: $D.params.slideId, }, - properties: { - annotations: noteData, + analysis: { + source: 'human', + execution_id: execId, // randomId + name: labelName, // labelName + labelId: labelId, + type: 'label', + isGrid: true, }, - geometries: convertGeometries(points, { - note: data.type, - size: feature.properties.size, - color: feature.properties.style.color, - }), - }; - } else { - // point / polygon / stringLine - annotJson = { - creator: getUserId(), - created_date: new Date(), - provenance: { - image: { - slide: $D.params.slideId, - }, - analysis: { - source: 'human', - execution_id: execId, // randomId - name: labelName, // labelName - labelId: labelId, - type: 'label', - }, + }, + properties: { + annotations: noteData, + }, + geometries: convertGeometries(points, { + note: data.type, + size: features.properties.size, + color: features.properties.style.color, + }), + }; + } else { + // point / polygon / stringLine + annotJson = { + creator: getUserId(), + created_date: new Date(), + provenance: { + image: { + slide: $D.params.slideId, }, - properties: { - annotations: noteData, + analysis: { + source: 'human', + execution_id: execId, // randomId + name: labelName, // labelName + labelId: labelId, + type: 'label', }, - geometries: ImageFeaturesToVieweportFeatures( - $CAMIC.viewer, - $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection(), - ), - }; - } - - $CAMIC.store - .addMark(annotJson) - .then((data) => { - // server error - if (data.error) { - $UI.message.addError(`${data.text}:${data.url}`); - Loading.close(); - return; - } - - // no data added - if (data.count < 1) { - Loading.close(); - $UI.message.addWarning(`Annotation Save Failed`); - return; - } - const __data = data.ops[0]; - // create layer data - const newItem = { - id: execId, - name: noteData.name, - typeId: 'human', - typeName: 'human', - creator: getUserId(), - shape: annotJson.geometries.features[0].geometry.type, - isGrid: annotJson.provenance.analysis.isGrid? true: false, - label: { - id: annotJson.provenance.analysis.labelId, - name: annotJson.provenance.analysis.name, - }, - data: null, - }; - $D.humanlayers.push(newItem); - $UI.layersViewer.addHumanItem(newItem, 'human', labelId); - $UI.layersViewerMinor.addHumanItem( - newItem, - 'human', - labelId, - $minorCAMIC && $minorCAMIC.viewer ? true : false, - ); - - __data._id = {$oid: __data._id}; - addAnnotation( - execId, - __data, - 'human', - labelId, - ); - }) - .catch((e) => { - Loading.close(); - console.log('save failed', e); - }) - .finally(() => { - $UI.message.addSmall(`Added The '${noteData.name}' Annotation.`); - }); + }, + properties: { + annotations: noteData, + }, + geometries: ImageFeaturesToVieweportFeatures( + $CAMIC.viewer, + $CAMIC.viewer.canvasDrawInstance.getImageFeatureCollection(), + ), + }; } + + $CAMIC.store + .addMark(annotJson) + .then((data) => { + // server error + if (data.error) { + $UI.message.addError(`${data.text}:${data.url}`); + Loading.close(); + return; + } + + // no data added + if (data.count < 1) { + Loading.close(); + $UI.message.addWarning(`Annotation Save Failed`); + return; + } + const __data = data.ops[0]; + // create layer data + const newItem = { + id: execId, + name: noteData.name, + typeId: 'human', + typeName: 'human', + creator: getUserId(), + shape: annotJson.geometries.features[0].geometry.type, + isGrid: annotJson.provenance.analysis.isGrid? true: false, + label: { + id: annotJson.provenance.analysis.labelId, + name: annotJson.provenance.analysis.name, + }, + data: null, + }; + $D.humanlayers.push(newItem); + $UI.layersViewer.addHumanItem(newItem, 'human', labelId); + $UI.layersViewerMinor.addHumanItem( + newItem, + 'human', + labelId, + $minorCAMIC && $minorCAMIC.viewer ? true : false, + ); + + __data._id = {$oid: __data._id}; + addAnnotation( + execId, + __data, + 'human', + labelId, + ); + }) + .catch((e) => { + Loading.close(); + console.log('save failed', e); + }) + .finally(() => { + $UI.message.addSmall(`Added The '${noteData.name}' Annotation.`); + }); } function addAnnotation(id, data, type, parent) { From b5f0edffa06b85bef27c0d3cf78882eebad9b09c Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Sat, 19 Aug 2023 14:41:35 +0100 Subject: [PATCH 31/46] Refer to iipsrv rather than OpenSlide --- core/CaMic.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/CaMic.js b/core/CaMic.js index cacf65ea9..b9cf3daf5 100644 --- a/core/CaMic.js +++ b/core/CaMic.js @@ -174,7 +174,7 @@ class CaMic { } fetch(checkSlideUrl, {credentials: 'include'}).then((z)=>{ if (true) { - this.openSlide(data, func); + this.iipSrv(data, func); } else { Loading.text.textContent = 'Slide Source Returned Status Code: ' + z.status; func.call(null, @@ -192,16 +192,16 @@ class CaMic { }); } - openSlide(data, func) { + iipSrv(data, func) { this.slideId = data['_id']['$oid']; this.slideName = data['name']; // insert token if present - let openSlideUrl = '../../img/IIP/raw/?DeepZoom='+ data['location'] + '.dzi'; + let iipSrvUrl = '../../img/IIP/raw/?DeepZoom='+ data['location'] + '.dzi'; if (getCookie('token')) { - openSlideUrl = '../../img/IIP/raw/?token=' + getCookie('token') + '&DeepZoom='+ data['location'] + '.dzi'; + iipSrvUrl = '../../img/IIP/raw/?token=' + getCookie('token') + '&DeepZoom='+ data['location'] + '.dzi'; } - this.viewer.open(openSlideUrl); + this.viewer.open(iipSrvUrl); // set mpp this.mpp_x = +data['mpp-x']; this.mpp_y = +data['mpp-y']; @@ -222,7 +222,7 @@ class CaMic { }); imagingHelper.setMaxZoom(1); - data.url = openSlideUrl; + data.url = iipSrvUrl; data.slide = this.slideId; if (func && typeof func === 'function') func.call(null, data); Loading.text.textContent = `Loading Slide's Tiles...`; From fcbedc74f4d904b8de3a4c12580ff75df4ccb241 Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Sun, 27 Aug 2023 12:11:54 +0100 Subject: [PATCH 32/46] BioFormats files --- apps/table.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/table.js b/apps/table.js index 7a0907bf3..b6273f411 100644 --- a/apps/table.js +++ b/apps/table.js @@ -13,7 +13,7 @@ function sanitize(string) { } var existingSlideNames = []; var permissions; -const allowedExtensions = ['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide', 'jpg', 'png', 'dcm']; +const allowedExtensions = ["svs", "tif", "tiff", "vms", "vmu", "ndpi", "scn", "mrxs", "bif", "svslide", "jpg", "png", "dcm", "v3draw", "ano", "cfg", "csv", "htm", "rec", "tim", "zpo", "dic", "dicom", "jp2", "j2ki", "j2kr", "raw", "ima", "cr2", "crw", "thm", "wav", "dv", "r3d", "r3d_d3d", "log", "mvd2", "aisf", "aiix", "dat", "atsf", "tf2", "tf8", "btf", "pbm", "pgm", "ppm", "xdce", "xml", "xlog", "apl", "tnb", "mtb", "im", "mea", "res", "aim", "arf", "psd", "al3d", "gel", "am", "amiramesh", "grey", "hx", "labels", "img", "hdr", "sif", "afi", "exp", "h5", "1sc", "pic", "ims", "ch5", "vsi", "ets", "pnl", "htd", "c01", "dib", "cxd", "v", "eps", "epsi", "ps", "flex", "xlef", "fits", "fts", "dm2", "dm3", "dm4", "naf", "his", "ndpis", "txt", "i2i", "hed", "mod", "inr", "ipl", "ipm", "fff", "ics", "ids", "seq", "ips", "ipw", "frm", "par", "j2k", "jpf", "jpk", "jpx", "klb", "xv", "bip", "sxm", "fli", "lim", "msr", "lif", "lof", "lei", "l2d", "mnc", "stk", "nd", "scan", "vff", "mrw", "stp", "mng", "nii", "nrrd", "nhdr", "nd2", "nef", "obf", "omp2info", "oib", "oif", "pty", "lut", "oir", "sld", "spl", "liff", "top", "pcoraw", "pcx", "pict", "pct", "df3", "im3", "qptiff", "bin", "env", "spe", "afm", "sm2", "sm3", "spc", "set", "sdt", "spi", "xqd", "xqf", "db", "vws", "pst", "inf", "tfr", "ffr", "zfr", "zfp", "2fl", "tga", "pr3", "dti", "fdf", "hdf", "xys", "html", "acff", "wat", "bmp", "wpi", "czi", "lms", "lsm", "mdb", "zvi", "mrc", "st", "ali", "map", "mrcs", "jpeg", "gif", "ptif"]; function validateForm(callback) { let slide = document.getElementById('slidename0'); // Check if input element is rendered or not From 7b89e37f82d07bc6f6d4600f822dd01ac00751ea Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Sun, 27 Aug 2023 12:12:58 +0100 Subject: [PATCH 33/46] BioFormats extensions batchloader.js --- apps/batchloader/batchLoader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/batchloader/batchLoader.js b/apps/batchloader/batchLoader.js index 8b1146aab..251120814 100644 --- a/apps/batchloader/batchLoader.js +++ b/apps/batchloader/batchLoader.js @@ -11,7 +11,7 @@ let finishUrl = '../../loader/upload/finish/'; let checkUrl = '../../loader/data/one/'; let chunkSize = 5*1024*1024; let finishUploadSuccess = false; -const allowedExtensions = ['svs', 'tif', 'tiff', 'vms', 'vmu', 'ndpi', 'scn', 'mrxs', 'bif', 'svslide', 'dcm']; +const allowedExtensions = ["svs", "tif", "tiff", "vms", "vmu", "ndpi", "scn", "mrxs", "bif", "svslide", "jpg", "png", "dcm", "v3draw", "ano", "cfg", "csv", "htm", "rec", "tim", "zpo", "dic", "dicom", "jp2", "j2ki", "j2kr", "raw", "ima", "cr2", "crw", "thm", "wav", "dv", "r3d", "r3d_d3d", "log", "mvd2", "aisf", "aiix", "dat", "atsf", "tf2", "tf8", "btf", "pbm", "pgm", "ppm", "xdce", "xml", "xlog", "apl", "tnb", "mtb", "im", "mea", "res", "aim", "arf", "psd", "al3d", "gel", "am", "amiramesh", "grey", "hx", "labels", "img", "hdr", "sif", "afi", "exp", "h5", "1sc", "pic", "ims", "ch5", "vsi", "ets", "pnl", "htd", "c01", "dib", "cxd", "v", "eps", "epsi", "ps", "flex", "xlef", "fits", "fts", "dm2", "dm3", "dm4", "naf", "his", "ndpis", "txt", "i2i", "hed", "mod", "inr", "ipl", "ipm", "fff", "ics", "ids", "seq", "ips", "ipw", "frm", "par", "j2k", "jpf", "jpk", "jpx", "klb", "xv", "bip", "sxm", "fli", "lim", "msr", "lif", "lof", "lei", "l2d", "mnc", "stk", "nd", "scan", "vff", "mrw", "stp", "mng", "nii", "nrrd", "nhdr", "nd2", "nef", "obf", "omp2info", "oib", "oif", "pty", "lut", "oir", "sld", "spl", "liff", "top", "pcoraw", "pcx", "pict", "pct", "df3", "im3", "qptiff", "bin", "env", "spe", "afm", "sm2", "sm3", "spc", "set", "sdt", "spi", "xqd", "xqf", "db", "vws", "pst", "inf", "tfr", "ffr", "zfr", "zfp", "2fl", "tga", "pr3", "dti", "fdf", "hdf", "xys", "html", "acff", "wat", "bmp", "wpi", "czi", "lms", "lsm", "mdb", "zvi", "mrc", "st", "ali", "map", "mrcs", "jpeg", "gif", "ptif"]; // call on document ready $(document).ready(function() { From 1799c88e68216df8209d3554526f80289b2d90b9 Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Sun, 27 Aug 2023 12:16:17 +0100 Subject: [PATCH 34/46] bioformats extensions batchloader.html --- apps/batchloader/batchloader.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/batchloader/batchloader.html b/apps/batchloader/batchloader.html index 7f99d28ff..bd329b73d 100644 --- a/apps/batchloader/batchloader.html +++ b/apps/batchloader/batchloader.html @@ -70,7 +70,7 @@ type="file" class="custom-file-input" id="filesInput" - accept=".svs, .tif, .tiff, .vms, .vmu, .ndpi, .scn, .mrxs, .bif, .svslide, .dcm" + accept=".svs, .tif, .tiff, .vms, .vmu, .ndpi, .scn, .mrxs, .bif, .svslide, .jpg, .png, .dcm, .v3draw, .ano, .cfg, .csv, .htm, .rec, .tim, .zpo, .dic, .dicom, .jp2, .j2ki, .j2kr, .raw, .ima, .cr2, .crw, .thm, .wav, .dv, .r3d, .r3d_d3d, .log, .mvd2, .aisf, .aiix, .dat, .atsf, .tf2, .tf8, .btf, .pbm, .pgm, .ppm, .xdce, .xml, .xlog, .apl, .tnb, .mtb, .im, .mea, .res, .aim, .arf, .psd, .al3d, .gel, .am, .amiramesh, .grey, .hx, .labels, .img, .hdr, .sif, .afi, .exp, .h5, .1sc, .pic, .ims, .ch5, .vsi, .ets, .pnl, .htd, .c01, .dib, .cxd, .v, .eps, .epsi, .ps, .flex, .xlef, .fits, .fts, .dm2, .dm3, .dm4, .naf, .his, .ndpis, .txt, .i2i, .hed, .mod, .inr, .ipl, .ipm, .fff, .ics, .ids, .seq, .ips, .ipw, .frm, .par, .j2k, .jpf, .jpk, .jpx, .klb, .xv, .bip, .sxm, .fli, .lim, .msr, .lif, .lof, .lei, .l2d, .mnc, .stk, .nd, .scan, .vff, .mrw, .stp, .mng, .nii, .nrrd, .nhdr, .nd2, .nef, .obf, .omp2info, .oib, .oif, .pty, .lut, .oir, .sld, .spl, .liff, .top, .pcoraw, .pcx, .pict, .pct, .df3, .im3, .qptiff, .bin, .env, .spe, .afm, .sm2, .sm3, .spc, .set, .sdt, .spi, .xqd, .xqf, .db, .vws, .pst, .inf, .tfr, .ffr, .zfr, .zfp, .2fl, .tga, .pr3, .dti, .fdf, .hdf, .xys, .html, .acff, .wat, .bmp, .wpi, .czi, .lms, .lsm, .mdb, .zvi, .mrc, .st, .ali, .map, .mrcs, .jpeg, .gif, .ptif" multiple required /> From b711883d7a6633bea68710f5dfcb9ab1fc0efeb5 Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:58:52 +0100 Subject: [PATCH 35/46] images in subdirs --- apps/batchloader/batchLoader.js | 9 ++++++++- apps/loader/chunked_upload.js | 6 +++++- apps/loader/loader.js | 9 ++++++--- apps/model/model.js | 14 ++++++++------ apps/viewer/uicallbacks.js | 16 ++++++++-------- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/apps/batchloader/batchLoader.js b/apps/batchloader/batchLoader.js index 251120814..64ac34dd9 100644 --- a/apps/batchloader/batchLoader.js +++ b/apps/batchloader/batchLoader.js @@ -21,8 +21,12 @@ $(document).ready(function() { store.findSlide().then((response) => { for (i=0; i { console.log(error); @@ -403,6 +407,9 @@ function finishUpload(token, filename, i) { fileNames[i] = newName; $('tr:eq('+(i+1)+') td:nth-child(2) span')[0].innerText = newName; } + if (a.relpath) { + fileNames[i] = a.relpath; + } if (typeof a === 'object' && a.error) { finishUploadSuccess = false; // $('#check_btn').hide(); diff --git a/apps/loader/chunked_upload.js b/apps/loader/chunked_upload.js index 9d54c074b..ab03c25fb 100644 --- a/apps/loader/chunked_upload.js +++ b/apps/loader/chunked_upload.js @@ -131,7 +131,11 @@ function finishUpload() { regReq.then((x)=>x.json()).then((a)=>{ changeStatus('UPLOAD | Finished', a, reset); reset = false; console.log(a); - if (a.filepath) { + if (a.relpath) { + document.getElementById('filename'+0).value = a.relpath; + } else if (a.filename) { + document.getElementById('filename'+0).value = a.filename; + } else if (a.filepath) { document.getElementById('filename'+0).value = a.filepath.slice(a.filepath.lastIndexOf('/')+1); } if (typeof a === 'object' && a.error) { diff --git a/apps/loader/loader.js b/apps/loader/loader.js index 47f96c4ba..ca1ddea45 100644 --- a/apps/loader/loader.js +++ b/apps/loader/loader.js @@ -122,12 +122,15 @@ function handleDownload(id) { store.getSlide(id) .then((response) => { if (response[0]) { - return response[0]['location']; + if (response[0]['filepath']) { + return response[0]['filepath']; + } + let location = response[0]['location']; + return location.substring(location.lastIndexOf('/')+1, location.length); } else { throw new Error('Slide not found'); } - }).then((location) => { - fileName = location.substring(location.lastIndexOf('/')+1, location.length); + }).then((fileName) => { console.log(fileName); return fileName; }).then((fileName) =>{ diff --git a/apps/model/model.js b/apps/model/model.js index 02315e996..56a58930a 100644 --- a/apps/model/model.js +++ b/apps/model/model.js @@ -408,15 +408,17 @@ function initCore() { $CAMIC.store.getSlide($D.params.slideId).then((response) => { if (response[0]) { - return response[0]['location']; + if (response[0]["filepath"]) { + return response[0]["filepath"]; + } + return location.substring( + location.lastIndexOf('/') + 1, + location.length, + ); } else { throw new Error('Slide not found'); } - }).then((location) => { - fileName = location.substring( - location.lastIndexOf('/') + 1, - location.length, - ); + }).then((fileName) => { console.log(fileName); }); diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js index f2e55ed4e..bb5d3d6e1 100644 --- a/apps/viewer/uicallbacks.js +++ b/apps/viewer/uicallbacks.js @@ -586,18 +586,18 @@ function imageDownload() { .getSlide(id) .then((response) => { if (response[0]) { - return response[0]['location']; + if (response[0]['filepath']) { + return response[0]['filepath'] + } + let location = response[0]['location']; + return location.substring( + location.lastIndexOf('/') + 1, + location.length, + ); } else { throw new Error('Slide not found'); } }) - .then((location) => { - fileName = location.substring( - location.lastIndexOf('/') + 1, - location.length, - ); - return fileName; - }) .then((fileName) => { fetch(downloadURL + fileName, { credentials: 'same-origin', From 737173f8e96efb4f722fb6bf2052957caa8525ed Mon Sep 17 00:00:00 2001 From: CGDogan <126820728+CGDogan@users.noreply.github.com> Date: Mon, 18 Sep 2023 15:04:55 +0100 Subject: [PATCH 36/46] DICOM button --- apps/table.html | 40 +++++++++++++++++++++++++++++++++++++++- apps/table.js | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/apps/table.html b/apps/table.html index 38b8b71ee..0ad2b8cb1 100644 --- a/apps/table.html +++ b/apps/table.html @@ -133,7 +133,13 @@

    caMicroscope

    -
    +
    +
    + +
    + `; - this.view.innerHTML = viewHtml; - this.elt.append(this.view); - - this.addBtn = this.view.querySelector('.add-model'); - this.enableBtn = this.view.querySelector(`#${modelEnableId}`); - this.modelSelectionBtn = this.view.querySelector(`#${modelSelectionId}`); - this.infoBtn = this.view.querySelector(`#${modelInfoId}`); - this.undoBtn = this.view.querySelector(`#${undoInfoId}`); - this.modelList = this.view.querySelector('.model-list'); - this.pixelScaleList = this.view.querySelector('.pixel-scale-list') - - this.annotateModeZone = this.view.querySelector('.annotate-checklist'); - this.settingZone = { - radius: this.view.querySelector('.radius-setting'), - threshold: this.view.querySelector('.threshold-setting'), - overlap: this.view.querySelector('.min-overlap-setting'), - } + this.view.innerHTML = viewHtml; + this.elt.append(this.view); + + this.addBtn = this.view.querySelector('.add-model'); + this.enableBtn = this.view.querySelector(`#${modelEnableId}`); + this.modelSelectionBtn = this.view.querySelector(`#${modelSelectionId}`); + this.infoBtn = this.view.querySelector(`#${modelInfoId}`); + this.undoBtn = this.view.querySelector(`#${undoInfoId}`); + this.modelList = this.view.querySelector('.model-list'); + this.pixelScaleList = this.view.querySelector('.pixel-scale-list'); + + this.annotateModeZone = this.view.querySelector('.annotate-checklist'); + this.settingZone = { + radius: this.view.querySelector('.radius-setting'), + threshold: this.view.querySelector('.threshold-setting'), + overlap: this.view.querySelector('.min-overlap-setting'), + }; + + const radiusLabel = this.settingZone.radius.querySelector('pre'); + tippy(radiusLabel, { + content: 'Enhance the coordinate percentage relative to the polygon drawn by the user. ' + + 'These expanded image will be used as input for image processing.', + placement: 'left', + delay: 300, + theme: 'translucent', + }); + + const thresholdLabel = this.settingZone.threshold.querySelector('pre'); + tippy(thresholdLabel, { + content: 'The separation threshold value represents the distinction between the object (foreground) ' + + 'and the surrounding area (background).', + placement: 'left', + delay: 300, + theme: 'translucent', + }); + + const overlapLabel = this.settingZone.overlap.querySelector('pre'); + tippy(overlapLabel, { + content: 'The minimum overlap refers to the required intersection between ' + + 'the user-selected polygon and the predicted polygons.', + placement: 'left', + delay: 300, + theme: 'translucent', + }); + this.modelPredictImgContainer = this.view.querySelector('.model-predict-image-container'), + + await this.__createModelList(); + + this.__assignEventListener(); +}; - const radiusLabel = this.settingZone.radius.querySelector('pre'); - tippy(radiusLabel, { - content: 'Enhance the coordinate percentage relative to the polygon drawn by the user. These expanded image will be used as input for image processing.', - placement: 'left', - delay: 300, - theme: 'translucent', - }); +Assistant.prototype.disableUndo = function() { + this.undoBtn.style.backgroundColor = '#9c9c9cb0'; + this.undoBtn.style.color = '#0088ff'; +}; - const thresholdLabel = this.settingZone.threshold.querySelector('pre'); - tippy(thresholdLabel, { - content: 'The separation threshold value represents the distinction between the object (foreground) and the surrounding area (background).', - placement: 'left', - delay: 300, - theme: 'translucent', - }); +Assistant.prototype.enableUndo = function() { + this.undoBtn.style.backgroundColor = '#ffffff'; + this.undoBtn.style.color = '#365f9c'; +}; - const overlapLabel = this.settingZone.overlap.querySelector('pre'); - tippy(overlapLabel, { - content: 'The minimum overlap refers to the required intersection between the user-selected polygon and the predicted polygons.', - placement: 'left', - delay: 300, - theme: 'translucent', +Assistant.prototype.__assignEventListener = function() { + // Open add model modal event + this.addBtn.addEventListener('click', (event) => { + // event.preventDefault(); + this.__openAddModel(); + }); + + // Enable Annotation Event + // TODO + + // Switch Current Model Event + this.modelList.querySelectorAll('label').forEach((elt) => { + elt.addEventListener('click', (event) => { + this.modelList.querySelectorAll('input').forEach((iElt) => { + iElt.checked = false; + }); + event.target.checked = true; + const modelKey = this.modelList.querySelector(`#${elt.getAttribute('for')}`).value; + this.__selectModel(modelKey); }); - this.modelPredictImgContainer = this.view.querySelector('.model-predict-image-container'), - - await this.__createModelList(); - - this.__assignEventListener(); -} + }); + + // Switch Pixel Scaling Event + this.pixelScaleList.querySelectorAll('label').forEach((elt) => { + elt.addEventListener('click', (event) => { + this.pixelScaleList.querySelectorAll('input').forEach((iElt) => { + iElt.checked = false; + }); + event.target.checked = true; + }); + }); + + + // Open Model Info Event + this.infoBtn.addEventListener('click', (event) => { + // event.preventDefault(); + this.__openModelInfo(); + }); + + // Switch Model Mode Event + this.annotateModeZone.querySelectorAll('input').forEach((elt) => { + elt.addEventListener('click', (event) => { + this.annotateModeZone.querySelectorAll('input').forEach((iElt) => { + iElt.checked = false; + iElt.removeAttribute('checked'); + }); + event.target.checked = true; + event.target.setAttribute('checked', 'true'); + }); + }); -Assistant.prototype.disableUndo = function() { - this.undoBtn.style.backgroundColor = '#9c9c9cb0'; - this.undoBtn.style.color = '#0088ff'; -} + // Change radius event + this.settingZone.radius.querySelector('input').addEventListener('change', (event) => { + this.settingZone.radius.querySelector('span').textContent = event.target.value; + if (this.__isEnableAssistant()) { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } + }); -Assistant.prototype.enableUndo = function() { - this.undoBtn.style.backgroundColor = '#ffffff'; - this.undoBtn.style.color = '#365f9c'; -} + // Change threshold event + this.settingZone.threshold.querySelector('input').addEventListener('change', (event) => { + this.settingZone.threshold.querySelector('span').textContent = event.target.value; + if (this.__isEnableAssistant()) { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } + }); -Assistant.prototype.__assignEventListener = function() { - // Open add model modal event - this.addBtn.addEventListener('click', (event) => { - // event.preventDefault(); - this.__openAddModel(); - }) - - // Enable Annotation Event - // TODO - - // Switch Current Model Event - this.modelList.querySelectorAll('label').forEach((elt) => { - elt.addEventListener('click', (event) => { - this.modelList.querySelectorAll('input').forEach((iElt) => { - iElt.checked = false; - }); - event.target.checked = true; - const modelKey = this.modelList.querySelector(`#${elt.getAttribute('for')}`).value; - this.__selectModel(modelKey); - }) - }) - - // Switch Pixel Scaling Event - this.pixelScaleList.querySelectorAll('label').forEach((elt) => { - elt.addEventListener('click', (event) => { - this.pixelScaleList.querySelectorAll('input').forEach((iElt) => { - iElt.checked = false; - }); - event.target.checked = true; - }) - }) - - - // Open Model Info Event - this.infoBtn.addEventListener('click', (event) => { - // event.preventDefault(); - this.__openModelInfo(); - }) - - // Switch Model Mode Event - this.annotateModeZone.querySelectorAll('input').forEach((elt) => { - elt.addEventListener('click', (event) => { - this.annotateModeZone.querySelectorAll('input').forEach((iElt) => { - iElt.checked = false; - iElt.removeAttribute('checked'); - }) - event.target.checked = true; - event.target.setAttribute('checked', 'true'); - }) - }) - - // Change radius event - this.settingZone.radius.querySelector('input').addEventListener('change', (event) => { - this.settingZone.radius.querySelector('span').textContent = event.target.value; - if (this.__isEnableAssistant()) { - this._viewer.raiseEvent('ml-draw-setting-change', {}); - } - }) - - // Change threshold event - this.settingZone.threshold.querySelector('input').addEventListener('change', (event) => { - this.settingZone.threshold.querySelector('span').textContent = event.target.value; - if (this.__isEnableAssistant()) { - this._viewer.raiseEvent('ml-draw-setting-change', {}); - } - }) - - // Change overlap event - this.settingZone.overlap.querySelector('input').addEventListener('change', (event) => { - this.settingZone.overlap.querySelector('span').textContent = event.target.value; - if (this.__isEnableAssistant()) { - this._viewer.raiseEvent('ml-draw-setting-change', {}); - } - }) -} + // Change overlap event + this.settingZone.overlap.querySelector('input').addEventListener('change', (event) => { + this.settingZone.overlap.querySelector('span').textContent = event.target.value; + if (this.__isEnableAssistant()) { + this._viewer.raiseEvent('ml-draw-setting-change', {}); + } + }); +}; // Add model list Assistant.prototype.__createModelList = async function() { - const dropDownList = [ - { - icon: 'timeline', - title: 'Default', - value: 'default', - checked: true, - }]; - - // modelName = []; - Object.keys(await tf.io.listModels()).forEach(function(element) { - const dict = {}; - const value = element.split('/').pop(); - if (value.slice(0, 3) == 'seg') { - const title = element.split('/').pop().split('_')[1].slice(0, -3); - dict.icon = 'flip_to_back'; - dict.title = title; - dict.value = value; - dict.checked = false; - dropDownList.push(dict); - } - }); + const dropDownList = [ + { + icon: 'timeline', + title: 'Default', + value: 'default', + checked: true, + }]; + + // modelName = []; + Object.keys(await tf.io.listModels()).forEach(function(element) { + const dict = {}; + const value = element.split('/').pop(); + if (value.slice(0, 3) == 'seg') { + const title = element.split('/').pop().split('_')[1].slice(0, -3); + dict.icon = 'flip_to_back'; + dict.title = title; + dict.value = value; + dict.checked = false; + dropDownList.push(dict); + } + }); - dropDownList.map((modelData) => { - this.__createModelLabel(modelData); - }) -} + dropDownList.map((modelData) => { + this.__createModelLabel(modelData); + }); +}; // Create model label Assistant.prototype.__createModelLabel = function(modelData) { - const modelId = randomId(); - const value = modelData.value; - const title = modelData.title; - const checked = modelData.checked; - const icon = modelData.icon; - const modelCardHtml = ` + const modelId = randomId(); + const value = modelData.value; + const title = modelData.title; + const checked = modelData.checked; + const icon = modelData.icon; + const modelCardHtml = ` - ` - const modelCard = document.createElement('li'); - modelCard.innerHTML = modelCardHtml; - if (checked) { - modelCard.querySelector('input').checked = true; - } - this.modelList.appendChild(modelCard); -} + `; + const modelCard = document.createElement('li'); + modelCard.innerHTML = modelCardHtml; + if (checked) { + modelCard.querySelector('input').checked = true; + } + this.modelList.appendChild(modelCard); +}; // Handle open model info modal Assistant.prototype.__openAddModel = function() { - //Raise open add model modal into view - this._viewer.raiseEvent('open-add-model', {}); -} + // Raise open add model modal into view + this._viewer.raiseEvent('open-add-model', {}); +}; // Add model Assistant.prototype.__updateModelList= function() { - empty(this.modelList); - this.__createModelList(); -} + empty(this.modelList); + this.__createModelList(); +}; Assistant.prototype.__isEnableAssistant = function() { - return this.enableBtn.checked; -} + return this.enableBtn.checked; +}; // Handle model selection Assistant.prototype.__selectModel = function(modelValue) { - mltools.loadModel(modelValue); -} + mltools.loadModel(modelValue); +}; // Handle open model info modal Assistant.prototype.__openModelInfo = function() { - this._viewer.raiseEvent('open-model-info', {}); -} + this._viewer.raiseEvent('open-model-info', {}); +}; // TODO // Handle model delete Assistant.prototype.__deleteModel = function(modelValue) { - // Remove UI - this.__updateModelList(); -} + // Remove UI + this.__updateModelList(); +}; // Get mode Assistant.prototype.__getAssistantMode = function() { - return this.annotateModeZone.querySelector('input[checked]').value; -} + return this.annotateModeZone.querySelector('input[checked]').value; +}; // Get setting mode value Assistant.prototype.__getSettingModes = function() { - const settingMode = { - radius: parseFloat(this.settingZone.radius.querySelector('input').value), - threshold: parseFloat(this.settingZone.threshold.querySelector('input').value), - overlap: parseFloat(this.settingZone.overlap.querySelector('input').value), - } - return settingMode; -} + const settingMode = { + radius: parseFloat(this.settingZone.radius.querySelector('input').value), + threshold: parseFloat(this.settingZone.threshold.querySelector('input').value), + overlap: parseFloat(this.settingZone.overlap.querySelector('input').value), + }; + return settingMode; +}; Assistant.prototype.__getScaleMethod = function() { - return this.pixelScaleList.querySelector('input[checked]').value; -} + return this.pixelScaleList.querySelector('input[checked]').value; +}; Assistant.prototype.__createElementFromHTML= function(htmlString) { - var div = document.createElement('div'); - div.innerHTML = htmlString.trim(); - return div.firstChild; + var div = document.createElement('div'); + div.innerHTML = htmlString.trim(); + return div.firstChild; }; diff --git a/components/ml-assistant/ml-tool.js b/components/ml-assistant/ml-tool.js index 03ef8ba2b..d36a2cab5 100644 --- a/components/ml-assistant/ml-tool.js +++ b/components/ml-assistant/ml-tool.js @@ -1,544 +1,545 @@ const IDB_URL = 'indexeddb://'; class mlTools { - constructor() { - this.init(); - } - - init() { - console.log('run init'); - this.canvas; - this.context; - this.data = new Map(); - this.threshold = this.t = 120; - this.radius = 30; - this.mode = 0; - this.model = 0; - this.modelLoaded = false; - this.size = 0; - this.ch = 4; - this.undo; - this.temp = document.createElement('canvas'); - this.fullPredict = document.createElement('canvas'); - this.sureFgImg1 = null; - this.sureFgImg2 = null; - } - - initcanvas(canvas) { - this.canvas = canvas; - this.context = canvas.getContext('2d'); - } - - /** - * - * @param {*} x1 - * @param {*} y1 - * @param {*} w - * @param {*} h - * @param {*} th - * @returns + constructor() { + this.init(); + } + + init() { + console.log('run init'); + this.canvas; + this.context; + this.data = new Map(); + this.threshold = this.t = 120; + this.radius = 30; + this.mode = 0; + this.model = 0; + this.modelLoaded = false; + this.size = 0; + this.ch = 4; + this.undo; + this.temp = document.createElement('canvas'); + this.fullPredict = document.createElement('canvas'); + this.sureFgImg1 = null; + this.sureFgImg2 = null; + } + + initcanvas(canvas) { + this.canvas = canvas; + this.context = canvas.getContext('2d'); + } + + /** + * + * @param {*} x1 + * @param {*} y1 + * @param {*} w + * @param {*} h + * @param {*} th + * @returns */ - detectContours(x1, y1, w, h, th, size, iter, context = this.context, invert = true) { - const imgCanvasData = context.getImageData(x1, y1, w, h); - let img = cv.matFromImageData(imgCanvasData); - - // Convert the image to grayscale - let gray = new cv.Mat(); - cv.cvtColor(img, gray, cv.COLOR_RGBA2GRAY); - - let thresholdImg1 = new cv.Mat(); - cv.threshold(gray, thresholdImg1, th, 255, cv.THRESH_BINARY) - const sureFgImg1 = this.thresholdImgToForegroundImg(thresholdImg1, size, iter, 2); - - let contours1 = new cv.MatVector(); - let hierarchy1 = new cv.Mat(); - cv.findContours(sureFgImg1, contours1, hierarchy1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); - - if (invert) { - let thresholdImg2 = new cv.Mat(); - cv.threshold(gray, thresholdImg2, th, 255, cv.THRESH_BINARY_INV) - const sureFgImg2 = this.thresholdImgToForegroundImg(thresholdImg2, size, iter, 2); - let contours2 = new cv.MatVector(); - let hierarchy2 = new cv.Mat(); - cv.findContours(sureFgImg2, contours2, hierarchy2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); - thresholdImg2.delete(); - hierarchy2.delete(); - sureFgImg2.delete(); - thresholdImg1.delete(); - hierarchy1.delete(); - sureFgImg1.delete(); - img.delete(); - gray.delete(); - - return [contours1, contours2] - } - - thresholdImg1.delete(); - hierarchy1.delete(); - sureFgImg1.delete(); - img.delete(); - gray.delete(); - - return [contours1]; + detectContours(x1, y1, w, h, th, size, iter, context = this.context, invert = true) { + const imgCanvasData = context.getImageData(x1, y1, w, h); + let img = cv.matFromImageData(imgCanvasData); + + // Convert the image to grayscale + let gray = new cv.Mat(); + cv.cvtColor(img, gray, cv.COLOR_RGBA2GRAY); + + let thresholdImg1 = new cv.Mat(); + cv.threshold(gray, thresholdImg1, th, 255, cv.THRESH_BINARY); + const sureFgImg1 = this.thresholdImgToForegroundImg(thresholdImg1, size, iter, 2); + + let contours1 = new cv.MatVector(); + let hierarchy1 = new cv.Mat(); + cv.findContours(sureFgImg1, contours1, hierarchy1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + + if (invert) { + let thresholdImg2 = new cv.Mat(); + cv.threshold(gray, thresholdImg2, th, 255, cv.THRESH_BINARY_INV); + const sureFgImg2 = this.thresholdImgToForegroundImg(thresholdImg2, size, iter, 2); + let contours2 = new cv.MatVector(); + let hierarchy2 = new cv.Mat(); + cv.findContours(sureFgImg2, contours2, hierarchy2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + thresholdImg2.delete(); + hierarchy2.delete(); + sureFgImg2.delete(); + thresholdImg1.delete(); + hierarchy1.delete(); + sureFgImg1.delete(); + img.delete(); + gray.delete(); + + return [contours1, contours2]; } - thresholdImgToForegroundImg(thresholdImg, erode_size = 2, iteration = 1, kernel_size = 3) { - // Perform morphological operations to enhance separation - const kernel = new cv.Mat(); - cv.Mat.ones(kernel_size, kernel_size, cv.CV_8U).copyTo(kernel); - const opening = new cv.Mat(); - cv.morphologyEx(thresholdImg, opening, cv.MORPH_OPEN, kernel); - const morph = new cv.Mat(); - cv.morphologyEx(opening, morph, cv.MORPH_CLOSE, kernel); - const erode = new cv.Mat(); - const erode_kernel = new cv.Mat(); - cv.Mat.ones(erode_size, erode_size, cv.CV_8U).copyTo(erode_kernel); - cv.erode(morph, erode, erode_kernel, new cv.Point(-1,-1), iteration); - kernel.delete(); - opening.delete(); - morph.delete(); - erode_kernel.delete(); - return erode; - } - - /** + thresholdImg1.delete(); + hierarchy1.delete(); + sureFgImg1.delete(); + img.delete(); + gray.delete(); + + return [contours1]; + } + + thresholdImgToForegroundImg(thresholdImg, erodeSize = 2, iteration = 1, kernel_size = 3) { + // Perform morphological operations to enhance separation + const kernel = new cv.Mat(); + cv.Mat.ones(kernel_size, kernel_size, cv.CV_8U).copyTo(kernel); + const opening = new cv.Mat(); + cv.morphologyEx(thresholdImg, opening, cv.MORPH_OPEN, kernel); + const morph = new cv.Mat(); + cv.morphologyEx(opening, morph, cv.MORPH_CLOSE, kernel); + const erode = new cv.Mat(); + const erodeKernel = new cv.Mat(); + cv.Mat.ones(erodeSize, erodeSize, cv.CV_8U).copyTo(erodeKernel); + cv.erode(morph, erode, erodeKernel, new cv.Point(-1, -1), iteration); + kernel.delete(); + opening.delete(); + morph.delete(); + erodeKernel.delete(); + return erode; + } + + /** * Compute the overlap area between contour and polygon * @param contour {number[][]} openCV contour data * @param polygon {number[][]} polygon data * @return {number} overlap area */ - overlapAreaAndCircumference(contour, polygon) { - const contour2 = contour.slice(); - const polygon2 = polygon.slice(); - contour2.push(contour2[0]); - polygon2.push(polygon2[0]); - const contourTurf = turf.polygon([contour2]); - const polygonTurf = turf.polygon([polygon2]); - const intersection = turf.intersect(contourTurf, polygonTurf); - if (!intersection) { - return 0.0; - } - const intersectionPolygon = intersection.geometry.coordinates[0]; - return { - area: this.polygonArea(intersectionPolygon), - circumference: this.getCircumference(intersectionPolygon), - }; + overlapAreaAndCircumference(contour, polygon) { + const contour2 = contour.slice(); + const polygon2 = polygon.slice(); + contour2.push(contour2[0]); + polygon2.push(polygon2[0]); + const contourTurf = turf.polygon([contour2]); + const polygonTurf = turf.polygon([polygon2]); + const intersection = turf.intersect(contourTurf, polygonTurf); + if (!intersection) { + return 0.0; } - - getCircumference(polygon) { - let length = 0; - for (let i = 0; i < polygon.length - 1; i++) { - length += Math.sqrt((polygon[i][0] - polygon[i+1][0])**2 + (polygon[i][1] - polygon[i+1][1])**2) - } - return length; + const intersectionPolygon = intersection.geometry.coordinates[0]; + return { + area: this.polygonArea(intersectionPolygon), + circumference: this.getCircumference(intersectionPolygon), + }; + } + + getCircumference(polygon) { + let length = 0; + for (let i = 0; i < polygon.length - 1; i++) { + length += Math.sqrt((polygon[i][0] - polygon[i+1][0])**2 + (polygon[i][1] - polygon[i+1][1])**2); } + return length; + } - /** + /** * Convert contour data into array * @param contour {any} openCV contour data * @return {number[][]} contour data array */ - convertToArray(contour) { - const contourData = contour.data32S; - let contourPoints = []; - for (let j = 0; j maxArea) { - maxArea = area; - if (fitContour) { - fitContour.delete(); - } - fitContour = contour.clone(); - } - contour.delete(); - } - contours[j].delete(); + mostFitContour(contours, polygon, expansionBound, erodeSize = 2, iteration = 1, overlap = 30) { + let maxArea = 0; + let fitContour; + let expandedContour; + const polygonArea = this.polygonArea(polygon); + for (let j = 0; j < contours.length; j++) { + for (let i = 0; i < contours[j].size(); i++) { + let contour = contours[j].get(i); + if (cv.contourArea(contour, false) < 50) { + continue; } - - if (fitContour) { - expandedContour = this.expandContour(fitContour, expansionBound.w, expansionBound.h, erode_size, iteration); - const fitContourArray = this.convertToArray(expandedContour); - expandedContour.delete(); - const expaned = this.overlapAreaAndCircumference(fitContourArray, polygon); - if (expaned.area/polygonArea < overlap/100) { - return []; - } - return [fitContourArray]; - } else { - return []; + const contourArray = this.convertToArray(contour); + if (this.closeBoundary(contourArray, expansionBound, 3)) { + continue; } - } - - manyFitContour(contours, polygon, expansionBound, erode_size = 2, iteration = 1, overlap = 30) { - let fitContours = []; - let expandedContours = []; - let expandedContourArrays = []; - let totalOverlapArea = 0; - const polygonArea = this.polygonArea(polygon); - for (let j = 0; j < contours.length; j++) { - for (let i = 0; i < contours[j].size(); i++) { - let contour = contours[j].get(i); - if (cv.contourArea(contour, false) < 50) { - continue; - } - const contourArray = this.convertToArray(contour); - if (this.closeBoundary(contourArray, expansionBound, 3)) { - continue; - } - const {area, circumference} = this.overlapAreaAndCircumference(contourArray, polygon); - if (!area || area < 15) { - continue; - } - const expanedArea = area + circumference*erode_size*iteration/2; - totalOverlapArea += expanedArea; - fitContours.push(contour.clone()); - contour.delete(); - } - contours[j].delete(); + const {area} = this.overlapAreaAndCircumference(contourArray, polygon); + if (area < 50) { + continue; } - if (!fitContours.length || totalOverlapArea/polygonArea < overlap/100) { - return []; + if (area > maxArea) { + maxArea = area; + if (fitContour) { + fitContour.delete(); + } + fitContour = contour.clone(); } - - expandedContours = fitContours.map((contour) => { - return this.expandContour(contour, expansionBound.w, expansionBound.h, erode_size, iteration); - }) - - expandedContourArrays = expandedContours.map((expanedContour) => { - const expandedContourArray = this.convertToArray(expanedContour); - expanedContour.delete(); - return expandedContourArray; - }) - - return expandedContourArrays; + contour.delete(); + } + contours[j].delete(); } - polygonArea(points) { - let area = 0; - let j = points.length - 2; - for (let i = 0; i < points.length - 1; i++) { - area += (points[j][0] + points[i][0]) * (points[j][1] - points[i][1]); - j = i; + if (fitContour) { + expandedContour = this.expandContour(fitContour, expansionBound.w, expansionBound.h, erodeSize, iteration); + const fitContourArray = this.convertToArray(expandedContour); + expandedContour.delete(); + const expaned = this.overlapAreaAndCircumference(fitContourArray, polygon); + if (expaned.area/polygonArea < overlap/100) { + return []; + } + return [fitContourArray]; + } else { + return []; + } + } + + manyFitContour(contours, polygon, expansionBound, erodeSize = 2, iteration = 1, overlap = 30) { + let fitContours = []; + let expandedContours = []; + let expandedContourArrays = []; + let totalOverlapArea = 0; + const polygonArea = this.polygonArea(polygon); + for (let j = 0; j < contours.length; j++) { + for (let i = 0; i < contours[j].size(); i++) { + let contour = contours[j].get(i); + if (cv.contourArea(contour, false) < 50) { + continue; + } + const contourArray = this.convertToArray(contour); + if (this.closeBoundary(contourArray, expansionBound, 3)) { + continue; } - return Math.abs(area / 2); + const {area, circumference} = this.overlapAreaAndCircumference(contourArray, polygon); + if (!area || area < 15) { + continue; + } + const expanedArea = area + circumference*erodeSize*iteration/2; + totalOverlapArea += expanedArea; + fitContours.push(contour.clone()); + contour.delete(); + } + contours[j].delete(); + } + if (!fitContours.length || totalOverlapArea/polygonArea < overlap/100) { + return []; } - expandContour(contour, width, height, size, iter) { - const mask = new cv.Mat.zeros(height, width, cv.CV_8UC1); - const point = new cv.Point(0, 0); - const bound = cv.boundingRect(contour); - for (let i = bound.x; i < bound.x + bound.width; i++) { - for (let j = bound.y; j < bound.y + bound.height; j++) { - point.x = i; - point.y = j; - if (cv.pointPolygonTest(contour, point, false) >= 0) { - mask.data[(point.y * mask.cols + point.x)] = 255; - } - } + expandedContours = fitContours.map((contour) => { + return this.expandContour(contour, expansionBound.w, expansionBound.h, erodeSize, iteration); + }); + + expandedContourArrays = expandedContours.map((expanedContour) => { + const expandedContourArray = this.convertToArray(expanedContour); + expanedContour.delete(); + return expandedContourArray; + }); + + return expandedContourArrays; + } + + polygonArea(points) { + let area = 0; + let j = points.length - 2; + for (let i = 0; i < points.length - 1; i++) { + area += (points[j][0] + points[i][0]) * (points[j][1] - points[i][1]); + j = i; + } + return Math.abs(area / 2); + } + + expandContour(contour, width, height, size, iter) { + const mask = new cv.Mat.zeros(height, width, cv.CV_8UC1); + const point = new cv.Point(0, 0); + const bound = cv.boundingRect(contour); + for (let i = bound.x; i < bound.x + bound.width; i++) { + for (let j = bound.y; j < bound.y + bound.height; j++) { + point.x = i; + point.y = j; + if (cv.pointPolygonTest(contour, point, false) >= 0) { + mask.data[(point.y * mask.cols + point.x)] = 255; } + } + } - const erode_kernel = new cv.Mat(); - cv.Mat.ones(size, size, cv.CV_8U).copyTo(erode_kernel); - - const dilate = new cv.Mat(); - cv.dilate(mask, dilate, erode_kernel, new cv.Point(-1,-1), iter); - // const processImage = document.querySelector('.processed-image-container'); - // this.showmatImg(dilate, processImage); - - let contours = new cv.MatVector(); - let hierarchy = new cv.Mat(); - cv.findContours(dilate, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); - mask.delete(); - erode_kernel.delete(); - dilate.delete(); - hierarchy.delete(); - if (contours.size() === 1) { - const resultContour = contours.get(0).clone(); - contours.delete(); - return resultContour; - } - contours.delete(); - return null; + const erodeKernel = new cv.Mat(); + cv.Mat.ones(size, size, cv.CV_8U).copyTo(erodeKernel); + + const dilate = new cv.Mat(); + cv.dilate(mask, dilate, erodeKernel, new cv.Point(-1, -1), iter); + // const processImage = document.querySelector('.processed-image-container'); + // this.showmatImg(dilate, processImage); + + let contours = new cv.MatVector(); + let hierarchy = new cv.Mat(); + cv.findContours(dilate, contours, hierarchy, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); + mask.delete(); + erodeKernel.delete(); + dilate.delete(); + hierarchy.delete(); + if (contours.size() === 1) { + const resultContour = contours.get(0).clone(); + contours.delete(); + return resultContour; } + contours.delete(); + return null; + } - /** + /** * Determine whether one point close to its boundary - * @param {number[][]} contour - * @param {any} expansionBound - * @param {number} epsilon + * @param {number[][]} contour + * @param {any} expansionBound + * @param {number} epsilon * @returns {boolean} */ - closeBoundary(contour, expansionBound, epsilon) { - let close = false; - for (let i = 0; i < contour.length; i++) { - if (contour[i][0] <= epsilon - || contour[i][0] >= expansionBound.w - epsilon - || contour[i][1] <= epsilon - || contour[i][1] >= expansionBound.h - epsilon) { - close = true; - break; - } - } - return close; + closeBoundary(contour, expansionBound, epsilon) { + let close = false; + for (let i = 0; i < contour.length; i++) { + if (contour[i][0] <= epsilon || + contour[i][0] >= expansionBound.w - epsilon || + contour[i][1] <= epsilon || + contour[i][1] >= expansionBound.h - epsilon) { + close = true; + break; + } } + return close; + } - /** + /** * Get coordinate parameter of polygon boundary * @param {number[][]} polygon - * @return {any} {x: left, y: top, w: width, h: height} + * @return {any} {x: left, y: top, w: width, h: height} */ - getCoordinate(polygon) { - let x1 = polygon[0][0]; - let y1 = polygon[0][1]; - let x2 = polygon[0][0]; - let y2 = polygon[0][1]; - - for (let i = 0; i < polygon.length; i++) { - if (x1 > polygon[i][0]) { - x1 = polygon[i][0]; - } - if (y1 > polygon[i][1]) { - y1 = polygon[i][1]; - } - if (x2 < polygon[i][0]) { - x2 = polygon[i][0]; - } - if (y2 < polygon[i][1]) { - y2 = polygon[i][1]; - } - } - return { - x: x1, - y: y1, - w: x2 - x1, - h: y2 - y1, - } + getCoordinate(polygon) { + let x1 = polygon[0][0]; + let y1 = polygon[0][1]; + let x2 = polygon[0][0]; + let y2 = polygon[0][1]; + + for (let i = 0; i < polygon.length; i++) { + if (x1 > polygon[i][0]) { + x1 = polygon[i][0]; + } + if (y1 > polygon[i][1]) { + y1 = polygon[i][1]; + } + if (x2 < polygon[i][0]) { + x2 = polygon[i][0]; + } + if (y2 < polygon[i][1]) { + y2 = polygon[i][1]; + } } - - /** + return { + x: x1, + y: y1, + w: x2 - x1, + h: y2 - y1, + }; + } + + /** * Return boundary information with expansion value * @param {any} originalBound * @param {number} expansionValue */ - getExpansionCoordicate(originalBound, expansionValue) { - return { - x: ~~(originalBound.x - originalBound.w * expansionValue/(100 * 2)), - y: ~~(originalBound.y - originalBound.h * expansionValue/(100 * 2)), - w: ~~(originalBound.w * (1 + expansionValue/100)), - h: ~~(originalBound.h * (1 + expansionValue/100)), - } - } - - /** + getExpansionCoordicate(originalBound, expansionValue) { + return { + x: ~~(originalBound.x - originalBound.w * expansionValue/(100 * 2)), + y: ~~(originalBound.y - originalBound.h * expansionValue/(100 * 2)), + w: ~~(originalBound.w * (1 + expansionValue/100)), + h: ~~(originalBound.h * (1 + expansionValue/100)), + }; + } + + /** * Realign position of polygon like array * @param {number[][]} array - input array * @param {number} x - left position of new coordinate * @param {number} y - top position of new coordinate * @return {number[][]} processed array */ - reAlign(array, x, y) { - if (array === undefined) { - return []; - } - for (let i = 0; i < array.length; i++) { - array[i][0] -= x; - array[i][1] -= y; - } - return array; + reAlign(array, x, y) { + if (array === undefined) { + return []; + } + for (let i = 0; i < array.length; i++) { + array[i][0] -= x; + array[i][1] -= y; } + return array; + } - /** + /** * Process drawing polygon without using any model * @param polygon {number[][]} drawing polygon data * @param threshold {number} threshold for edge detection * @param expansion {number} expansion percentage from existing data * @return {number[][]} processed polygon */ - applyDrawNoModel(polygon, threshold, expansion, overlap, drawMany = false) { - // remove last point from the polygon - polygon.pop(); - - // get current polygon coordinate (left, top, width, height) - const polygonBound = this.getCoordinate(polygon); - - // get expansion coordinate (left, top, width, height) - const expansionBound = this.getExpansionCoordicate(polygonBound, expansion); - - // re-align polygon origin - polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); - - let fitContours; - for (let size = 0; size < 20; size+=6) { - for (let iter = 1; iter < 10; iter+=4) { - for (let dth = 1; dth < 100; dth+=10) { - const th = (dth % 2 === 1) ? threshold - ~~(dth/2) : threshold - ~~(dth/2); - if (th <= 0 || th >= 255) { - continue; - } - // get contours from detect edges image - const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, expansionBound.h, th, size, iter); - // get most fit contour - if (!drawMany) { - fitContours = this.mostFitContour(contours, polygon, expansionBound, size, iter, overlap); - } else { - fitContours = this.manyFitContour(contours, polygon, expansionBound, size, iter, overlap); - } - if (fitContours.length) { - break; - } - } - if (fitContours.length) { - break; - } - } - if (fitContours.length) { - break; - } + applyDrawNoModel(polygon, threshold, expansion, overlap, drawMany = false) { + // remove last point from the polygon + polygon.pop(); + + // get current polygon coordinate (left, top, width, height) + const polygonBound = this.getCoordinate(polygon); + + // get expansion coordinate (left, top, width, height) + const expansionBound = this.getExpansionCoordicate(polygonBound, expansion); + + // re-align polygon origin + polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); + + let fitContours; + for (let size = 0; size < 20; size+=6) { + for (let iter = 1; iter < 10; iter+=4) { + for (let dth = 1; dth < 100; dth+=10) { + const th = (dth % 2 === 1) ? threshold - ~~(dth/2) : threshold - ~~(dth/2); + if (th <= 0 || th >= 255) { + continue; + } + // get contours from detect edges image + const contours = this.detectContours(expansionBound.x, expansionBound.y, expansionBound.w, + expansionBound.h, th, size, iter); + // get most fit contour + if (!drawMany) { + fitContours = this.mostFitContour(contours, polygon, expansionBound, size, iter, overlap); + } else { + fitContours = this.manyFitContour(contours, polygon, expansionBound, size, iter, overlap); + } + if (fitContours.length) { + break; + } } - - if (fitContours.length === 0) { - return []; + if (fitContours.length) { + break; } - // re-align the most fit contour - fitContours = fitContours.map((fitContour) => { - fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); - fitContour.push(fitContour[0]); - return fitContour; - }) - - return fitContours; + } + if (fitContours.length) { + break; + } + } + + if (fitContours.length === 0) { + return []; } + // re-align the most fit contour + fitContours = fitContours.map((fitContour) => { + fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + fitContour.push(fitContour[0]); + return fitContour; + }); - /** + return fitContours; + } + + /** * Load model when annotation have been enable * @param {string} key - model key * @return {Promise} */ - loadModel(key) { - if (key === 'default') { - try { - this.model.dispose(); - } catch (error) { } - this.model = 'default'; - return Promise.resolve(true); - } - return new Promise((resolve, reject) => { - try { - if (this.model && this.model !== 'default') { - try { - this.model.dispose(); - } catch (error) { } - } - const tx = db.transaction('models_store', 'readonly'); - const store = tx.objectStore('models_store'); - const req = store.get(key); - - req.onsuccess = async function(e) { - // self.showProgress('Loading model...'); - - // Keras sorts the labels by alphabetical order. - const inputShape = e.target.result.input_shape; - console.log('inputShape: ', inputShape); - this.size = parseInt(inputShape[1]); - this.ch = parseInt(inputShape[3]); - - this.model = await tf.loadLayersModel(IDB_URL + key); - console.log('Model Loaded'); - const memory = tf.memory(); - console.log('Model Memory Usage'); - console.log('GPU : ' + memory.numBytesInGPU + ' bytes'); - console.log('Total : ' + memory.numBytes + ' bytes'); - - // tfvis.show.modelSummary({name: 'Model Summary', tab: 'Model Inspection'}, model); - tf.tidy(()=>{ - // Warmup the model before using real data. - this.model.predict(tf.zeros([1, this.size, this.size, this.ch])); - // self.showProgress('Model loaded...'); - resolve(true) - }); - }.bind(this) - } catch (error) { - console.log('fail to load model: ', error); - reject(false); - } - }) + loadModel(key) { + if (key === 'default') { + try { + this.model.dispose(); + } catch (error) { } + this.model = 'default'; + return Promise.resolve(true); } - - /** + return new Promise((resolve, reject) => { + try { + if (this.model && this.model !== 'default') { + try { + this.model.dispose(); + } catch (error) { } + } + const tx = db.transaction('models_store', 'readonly'); + const store = tx.objectStore('models_store'); + const req = store.get(key); + + req.onsuccess = async function(e) { + // self.showProgress('Loading model...'); + + // Keras sorts the labels by alphabetical order. + const inputShape = e.target.result.input_shape; + console.log('inputShape: ', inputShape); + this.size = parseInt(inputShape[1]); + this.ch = parseInt(inputShape[3]); + + this.model = await tf.loadLayersModel(IDB_URL + key); + console.log('Model Loaded'); + const memory = tf.memory(); + console.log('Model Memory Usage'); + console.log('GPU : ' + memory.numBytesInGPU + ' bytes'); + console.log('Total : ' + memory.numBytes + ' bytes'); + + // tfvis.show.modelSummary({name: 'Model Summary', tab: 'Model Inspection'}, model); + tf.tidy(()=>{ + // Warmup the model before using real data. + this.model.predict(tf.zeros([1, this.size, this.size, this.ch])); + // self.showProgress('Model loaded...'); + resolve(true); + }); + }.bind(this); + } catch (error) { + console.log('fail to load model: ', error); + reject(error); + } + }); + } + + /** * Get expansion coordinate parameter coresponding with using model * @param {number} step - current model input size px * @param {any} polygonBound - current polygon boundary parameter * @param {number} expansionValue - choosen expansion value * @return {any} expansion boundary parameter */ - getModelExpansionCoordicate(step, polygonBound, expansionValue) { - const extendX = Math.ceil(polygonBound.w * (1 + expansionValue / 100) / step) * step - polygonBound.w; - const extendY = Math.ceil(polygonBound.h * (1 + expansionValue / 100) / step) * step - polygonBound.h; - return { - x: polygonBound.x - ~~(extendX/2), - y: polygonBound.y - ~~(extendY/2), - w: polygonBound.w + extendX, - h: polygonBound.h + extendY, - } - } - - /** + getModelExpansionCoordicate(step, polygonBound, expansionValue) { + const extendX = Math.ceil(polygonBound.w * (1 + expansionValue / 100) / step) * step - polygonBound.w; + const extendY = Math.ceil(polygonBound.h * (1 + expansionValue / 100) / step) * step - polygonBound.h; + return { + x: polygonBound.x - ~~(extendX/2), + y: polygonBound.y - ~~(extendY/2), + w: polygonBound.w + extendX, + h: polygonBound.h + extendY, + }; + } + + /** * Get list of coordinates * @param {number} step - model input size * @param {any} expansionBound - expansion boundary parameter * @return {any[]} - list of grid pieces coordinates */ - getGridCoordinate(step, expansionBound) { - const numStepX = ~~(expansionBound.w / step); - const numStepY = ~~(expansionBound.h / step); - let gridBounds = []; - for (let i = 0; i < numStepX; i++) { - for (let j = 0; j < numStepY; j++) { - gridBounds.push({ - x: expansionBound.x + i * step, - y: expansionBound.y + j * step, - w: step, - h: step, - }) - } - } - return gridBounds; + getGridCoordinate(step, expansionBound) { + const numStepX = ~~(expansionBound.w / step); + const numStepY = ~~(expansionBound.h / step); + let gridBounds = []; + for (let i = 0; i < numStepX; i++) { + for (let j = 0; j < numStepY; j++) { + gridBounds.push({ + x: expansionBound.x + i * step, + y: expansionBound.y + j * step, + w: step, + h: step, + }); + } } + return gridBounds; + } - /** + /** * Using imported model for autocorrect user draw polygon * @param {any} model - processing model * @param {number} size - model input image size px @@ -548,200 +549,203 @@ class mlTools { * @param {number} threshold - upper threshold value for canny detection * @param {number} expansion - expansion percentage */ - async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion, overlap, drawMany = false) { - // remove last point from the polygon - polygon.pop(); - - // get current polygon coordinate (left, top, width, height) - const polygonBound = this.getCoordinate(polygon); - - // get expansion coordinate (left, top, width, height) - const expansionBound = this.getModelExpansionCoordicate(size, polygonBound, expansion); - - // get grid coordinate with grid size is model size - const gridBounds = this.getGridCoordinate(size, expansionBound); - - // loop over all pieces of image and run the model - this.fullPredict.getContext('2d').clearRect(0, 0, this.fullPredict.width, this.fullPredict.height); - this.fullPredict.width = expansionBound.w; - this.fullPredict.height = expansionBound.h; - for (let i = 0; i < gridBounds.length; i++) { - // get image data - const imgCanvasData = this.context.getImageData(gridBounds[i].x, gridBounds[i].y, size, size); - let val; - tf.tidy(() => { - const img = tf.browser.fromPixels(imgCanvasData).toFloat(); - let img2; - if (ch == 1) { - img2 = tf.image.resizeBilinear(img, [size, size]).mean(2); - } else { - img2 = tf.image.resizeBilinear(img, [size, size]); - } - let normalized; - if (scaleMethod == 'norm') { - const scale = tf.scalar(255); - normalized = img2.div(scale); - scale.dispose(); - } else if (scaleMethod == 'center') { - const mean = img2.mean(); - normalized = img2.sub(mean); - mean.dispose(); - } else if (scaleMethod == 'std') { - const mean = img2.mean(); - const std = (img2.squaredDifference(mean).sum()).div(img2.flatten().shape).sqrt(); - normalized = img2.sub(mean).div(std); - mean.dispose(); - std.dispose(); - } else { - normalized = img2; - } - const batched = normalized.reshape([1, size, size, ch]); - let values = model.predict(batched).dataSync(); - let valuesArray = Array.from(values); - // scale values - valuesArray = valuesArray.map((x) => x * 255); - val = []; - while (valuesArray.length > 0) val.push(valuesArray.splice(0, size)); - const padding = 2; - val = this.fillBoundary(val, padding); - img.dispose(); - img2.dispose(); - normalized.dispose(); - batched.dispose(); - }) - tf.engine().startScope(); - await tf.browser.toPixels(val, this.temp); - this.fullPredict.getContext('2d').drawImage(this.temp, gridBounds[i].x - expansionBound.x, gridBounds[i].y - expansionBound.y); - tf.engine().endScope(); - } - - this.showCanvas(this.fullPredict, document.querySelector('.model-predict-image-container')); - const fullPredictCanvas = this.fullPredict.getContext('2d'); - - // re-align polygon origin - polygon = this.reAlign(polygon, expansionBound.x, expansionBound.y); - - let fitContours = []; - - for (let size = 0; size < 20; size+=6) { - for (let iter = 1; iter < 10; iter+=4) { - for (let dth = 0; dth < 200; dth+=10) { - const th = (dth % 2 === 1) ? threshold - ~~(dth/2) : threshold + ~~(dth/2); - if (th <= 0 || th >= 255) { - continue; - } - // get contours from detect edges image - const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, th, size, iter, fullPredictCanvas, false); - // get most fit contour - if (!drawMany) { - fitContours = this.mostFitContour(contours, polygon, expansionBound, size, iter, overlap); - } else { - fitContours = this.manyFitContour(contours, polygon, expansionBound, size, iter, overlap); - } - if (fitContours.length) { - break; - } - } - if (fitContours.length) { - break; - } - } - if (fitContours.length) { - break; - } - } - - if (fitContours.length === 0) { - return []; + async applyDrawModel(model, size, ch, scaleMethod, polygon, threshold, expansion, overlap, drawMany = false) { + // remove last point from the polygon + polygon.pop(); + + // get current polygon coordinate (left, top, width, height) + const polygonBound = this.getCoordinate(polygon); + + // get expansion coordinate (left, top, width, height) + const expansionBound = this.getModelExpansionCoordicate(size, polygonBound, expansion); + + // get grid coordinate with grid size is model size + const gridBounds = this.getGridCoordinate(size, expansionBound); + + // loop over all pieces of image and run the model + this.fullPredict.getContext('2d').clearRect(0, 0, this.fullPredict.width, this.fullPredict.height); + this.fullPredict.width = expansionBound.w; + this.fullPredict.height = expansionBound.h; + for (let i = 0; i < gridBounds.length; i++) { + // get image data + const imgCanvasData = this.context.getImageData(gridBounds[i].x, gridBounds[i].y, size, size); + let val; + tf.tidy(() => { + const img = tf.browser.fromPixels(imgCanvasData).toFloat(); + let img2; + if (ch == 1) { + img2 = tf.image.resizeBilinear(img, [size, size]).mean(2); + } else { + img2 = tf.image.resizeBilinear(img, [size, size]); } - - // re-align the most fit contour - fitContours = fitContours.map((fitContour) => { - fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); - fitContour.push(fitContour[0]); - return fitContour; - }); - - return fitContours; - } - - async applyDraw(polygon, threshold, expansion, overlap, scaleMethod = 'no_scale', drawMany = false) { - if (this.model && this.model !== 'default') { - return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, expansion, overlap, drawMany); + let normalized; + if (scaleMethod == 'norm') { + const scale = tf.scalar(255); + normalized = img2.div(scale); + scale.dispose(); + } else if (scaleMethod == 'center') { + const mean = img2.mean(); + normalized = img2.sub(mean); + mean.dispose(); + } else if (scaleMethod == 'std') { + const mean = img2.mean(); + const std = (img2.squaredDifference(mean).sum()).div(img2.flatten().shape).sqrt(); + normalized = img2.sub(mean).div(std); + mean.dispose(); + std.dispose(); } else { - return this.applyDrawNoModel(polygon, threshold, expansion, overlap, drawMany); + normalized = img2; } + const batched = normalized.reshape([1, size, size, ch]); + let values = model.predict(batched).dataSync(); + let valuesArray = Array.from(values); + // scale values + valuesArray = valuesArray.map((x) => x * 255); + val = []; + while (valuesArray.length > 0) val.push(valuesArray.splice(0, size)); + const padding = 2; + val = this.fillBoundary(val, padding); + img.dispose(); + img2.dispose(); + normalized.dispose(); + batched.dispose(); + }); + tf.engine().startScope(); + await tf.browser.toPixels(val, this.temp); + this.fullPredict.getContext('2d'). + drawImage(this.temp, gridBounds[i].x - expansionBound.x, gridBounds[i].y - expansionBound.y); + tf.engine().endScope(); } - fillBoundary(imageArray, padding) { - const size = imageArray.length; - for (let i = 0; i < padding; i++) { - for (let j = padding; j= 255) { + continue; + } + // get contours from detect edges image + const contours = this.detectContours(0, 0, this.fullPredict.width, this.fullPredict.height, + th, size, iter, fullPredictCanvas, false); + // get most fit contour + if (!drawMany) { + fitContours = this.mostFitContour(contours, polygon, expansionBound, size, iter, overlap); + } else { + fitContours = this.manyFitContour(contours, polygon, expansionBound, size, iter, overlap); + } + if (fitContours.length) { + break; + } } - for (let i = 0; i < padding; i++) { - for (let j = 0; j < padding; j++) { - imageArray[i][j] = imageArray[padding][padding]; - imageArray[size-i-1][j] = imageArray[size-padding-1][padding]; - imageArray[i][size-j-1] = imageArray[padding][size-padding-1]; - imageArray[size-i-1][size-j-1] = imageArray[size-padding-1][size-padding-1]; - } + if (fitContours.length) { + break; } - return imageArray; + } + if (fitContours.length) { + break; + } } - showmatImg(edges, elt, convertCh = true) { - // Create a new canvas for displaying the edges - empty(elt) - var edgesCanvas = document.createElement('canvas'); - edgesCanvas.width = edges.cols; - edgesCanvas.height = edges.rows; - var edgesCtx = edgesCanvas.getContext('2d'); - let data = [] - if (convertCh) { - for (let i = 0; i < edges.data.length; i++) { - data.push(edges.data[i]); - data.push(edges.data[i]); - data.push(edges.data[i]); - data.push(255); - } - } + if (fitContours.length === 0) { + return []; + } - // Convert the edges data to an image - var edgesData = new ImageData( - new Uint8ClampedArray(data), - edges.cols, - edges.rows - ); - - // Draw the edges on the canvas - edgesCtx.putImageData(edgesData, 0, 0); - if ((edgesCanvas.height/edgesCanvas.width) > (elt.offsetHeight/elt.offsetWidth)) { - edgesCanvas.style.height = '100%'; - edgesCanvas.style.width = ''; - } else { - edgesCanvas.style.width = '100%'; - edgesCanvas.style.height = ''; - } - // Append the canvas to the document body or any other container - elt.appendChild(edgesCanvas); + // re-align the most fit contour + fitContours = fitContours.map((fitContour) => { + fitContour = this.reAlign(fitContour, -expansionBound.x, -expansionBound.y); + fitContour.push(fitContour[0]); + return fitContour; + }); + + return fitContours; + } + + async applyDraw(polygon, threshold, expansion, overlap, scaleMethod = 'no_scale', drawMany = false) { + if (this.model && this.model !== 'default') { + return await this.applyDrawModel(this.model, this.size, this.ch, scaleMethod, polygon, threshold, + expansion, overlap, drawMany); + } else { + return this.applyDrawNoModel(polygon, threshold, expansion, overlap, drawMany); + } + } + + fillBoundary(imageArray, padding) { + const size = imageArray.length; + for (let i = 0; i < padding; i++) { + for (let j = padding; j (elt.offsetHeight/elt.offsetWidth)) { - canvas.style.height = '100%'; - canvas.style.width = ''; - } else { - canvas.style.width = '100%'; - canvas.style.height = ''; - } - elt.appendChild(canvas); + // Convert the edges data to an image + var edgesData = new ImageData( + new Uint8ClampedArray(data), + edges.cols, + edges.rows, + ); + + // Draw the edges on the canvas + edgesCtx.putImageData(edgesData, 0, 0); + if ((edgesCanvas.height/edgesCanvas.width) > (elt.offsetHeight/elt.offsetWidth)) { + edgesCanvas.style.height = '100%'; + edgesCanvas.style.width = ''; + } else { + edgesCanvas.style.width = '100%'; + edgesCanvas.style.height = ''; + } + // Append the canvas to the document body or any other container + elt.appendChild(edgesCanvas); + } + + showCanvas(canvas, elt) { + empty(elt); + if ((canvas.height/canvas.width) > (elt.offsetHeight/elt.offsetWidth)) { + canvas.style.height = '100%'; + canvas.style.width = ''; + } else { + canvas.style.width = '100%'; + canvas.style.height = ''; } + elt.appendChild(canvas); + } } var mltools = new mlTools(); diff --git a/components/sidemenu/sidemenu.js b/components/sidemenu/sidemenu.js index 07358d27a..f025fd35e 100644 --- a/components/sidemenu/sidemenu.js +++ b/components/sidemenu/sidemenu.js @@ -118,7 +118,7 @@ SideMenu.prototype.__refresh = function() { icon1.classList.add('material-icons'); icon1.classList.add('md-24'); icon1.textContent = 'chevron_left'; - + const icon2 = icon1.cloneNode(true); icon2.classList.add('sec'); From 78157bb0d1a99d3bee6b4899898ab7f88627bdef Mon Sep 17 00:00:00 2001 From: Birm Date: Tue, 10 Oct 2023 12:59:42 -0400 Subject: [PATCH 42/46] some guidance to ml model upload --- apps/viewer/init.js | 7 ++++++- components/ml-assistant/ml-assistant.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/viewer/init.js b/apps/viewer/init.js index fcfc83066..c948b229b 100644 --- a/apps/viewer/init.js +++ b/apps/viewer/init.js @@ -989,10 +989,15 @@ function initModelModals() { $UI.uploadModal = new ModalBox({ id: 'upload_panel', hasHeader: true, - headerText: 'Upload Model', + headerText: 'Upload Segmentation Model', hasFooter: false, provideContent: true, content: ` + Add a segmentation model in + TFJS format. + Some examples can be found here. + +

    • diff --git a/components/ml-assistant/ml-assistant.js b/components/ml-assistant/ml-assistant.js index efcec975b..f2fc2fa24 100644 --- a/components/ml-assistant/ml-assistant.js +++ b/components/ml-assistant/ml-assistant.js @@ -60,7 +60,7 @@ Assistant.prototype.__refreshUI = async function() { const stdId = randomId(); const viewHtml = ` - +
      • From 86f977f726985d60ae41e707c34a7ae36914a9f1 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Tue, 7 Nov 2023 22:18:03 -0600 Subject: [PATCH 43/46] hide the smart pen icon --- common/smartpen/autoalign.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/smartpen/autoalign.css b/common/smartpen/autoalign.css index be4fb6dff..63899f6bc 100644 --- a/common/smartpen/autoalign.css +++ b/common/smartpen/autoalign.css @@ -1,4 +1,7 @@ /*upper blocks*/ +#align_menu { + display: none; +} #align_menu *{ font-size:24px; color:#0000be;/**/ From 4f1dd57e5815b53d098cbe88d95bcc49855e1067 Mon Sep 17 00:00:00 2001 From: Birm Date: Thu, 9 Nov 2023 12:00:24 -0500 Subject: [PATCH 44/46] bump year to 2023 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 93513746c..59f5f49a3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2012-2021, caMicroscope +Copyright (c) 2012-2023, caMicroscope All rights reserved. Redistribution and use in source and binary forms, with or without From 1262a6e6c0dc4702ec90a096fb1917bb47c3a664 Mon Sep 17 00:00:00 2001 From: Birm Date: Fri, 17 Nov 2023 16:14:21 -0500 Subject: [PATCH 45/46] Update HISTORY.md for 3.11.0 --- HISTORY.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 3bba70c98..902ee3ddf 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -25,9 +25,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **Version 1** * [1.0.x](#camicroscope-10) -### caMicroscope [Unreleased](https://github.com/camicroscope/camicroscope/compare/v3.10.2...camicroscope:develop) +### caMicroscope [Unreleased](https://github.com/camicroscope/camicroscope/compare/v3.11.0...camicroscope:develop) ###### TBD +### caMicroscope [3.11.0](https://github.com/camicroscope/camicroscope/compare/v3.10.2...camicroscope:v3.11.0) +###### 2023-11-17 +* Dicom and Bioformats Intergration (#647, #653, #655) +* Improved Machine Learning Toolkit (#658) + ### caMicroscope [3.10.2](https://github.com/camicroscope/camicroscope/compare/v3.10.1...camicroscope:v3.10.2) ###### 2023-06-16 * point to point From a46d702d954e142196d08ff02cbad7c64b6cca2d Mon Sep 17 00:00:00 2001 From: Birm Date: Fri, 17 Nov 2023 16:15:14 -0500 Subject: [PATCH 46/46] Index doesn't work, remove it. --- HISTORY.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 902ee3ddf..ba2ca3302 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,26 +5,6 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## Index -* **Version 3** - * [3.10.x](#camicroscope-3100) - * [3.9.x](#camicroscope-390) - * [3.8.x](#camicroscope-380) - * [3.7.x](#camicroscope-377) - * [3.7.x](#camicroscope-375) - * [3.6.x](#camicroscope-362) - * [3.5.x](#camicroscope-3510) - * [3.4.x](#camicroscope-343) - * [3.3.x](#camicroscope-334) - * [3.2.x](#camicroscope-322) - * [3.1.x](#camicroscope-311) - * [3.0.x](#camicroscope-300) -* **Version 2** - * [2.0.x](#camicroscope-201) -* **Version 1** - * [1.0.x](#camicroscope-10) - ### caMicroscope [Unreleased](https://github.com/camicroscope/camicroscope/compare/v3.11.0...camicroscope:develop) ###### TBD