diff --git a/apps/dicom-connect/table.css b/apps/dicom-connect/table.css
new file mode 100644
index 000000000..786b61235
--- /dev/null
+++ b/apps/dicom-connect/table.css
@@ -0,0 +1,260 @@
+html,
+body {
+ width: 100%;
+ height: 100%;
+ min-height: 100%;
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+footer {
+ /* position: sticky;
+ /* bottom: -8px; */
+ width: 100%;
+}
+.header {
+ margin-top: 60px;
+}
+
+.page-container {
+ height: 100vh;
+ /* flex-direction: column; */
+ /* justify-content: space-between; */
+}
+
+.link {
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+ font-size: 0.99em;
+ font-weight: bold;
+ color: #5e6875;
+ text-decoration: none;
+ padding-right: 0.7rem;
+ padding-left: 0.7rem;
+ margin-top: 0.2rem;
+ font-family: "Open Sans", Helvetica, sans-serif;
+ /* line-height: 1.8; */
+}
+.bg-dark {
+ background-color: #343a40 !important;
+}
+.bg-info {
+ background-color: #17a2b8 !important;
+}
+#collection-list li.item {
+ cursor: pointer;
+}
+#collection-list li.item:hover {
+ background-color: #deeeff;
+}
+#entries {
+ cursor: pointer;
+}
+.page-item {
+ cursor: pointer;
+}
+
+/* #collection-list .item:hover {
+ background-color:#f8f9fa;
+} */
+
+#collection-list .item {
+ /* font-weight:bold; */
+ color: #007bff;
+ padding: 0.5rem;
+}
+#collection-list .item i {
+ padding: 0.25rem;
+}
+
+nav li {
+ transition: background 0.5s;
+ border-radius: 3px;
+}
+nav li:not(:first-child) {
+ margin-left: 0.3em !important;
+}
+nav li:not(.active):hover {
+ background: white;
+}
+nav li:not(.active):hover a {
+ color: black !important;
+}
+.active {
+ background: white;
+}
+.active a {
+ color: black !important;
+}
+
+nav li:not(.active):hover{
+ background: white;
+}
+
+.overall {
+ display: flex;
+}
+
+.reload {
+ display: none;
+ padding: 0 4px;
+}
+
+.btn2{
+ display: flex;
+ align-items: center;
+}
+@media screen and (min-width: 480px){
+ .reload {
+ display: block;
+ }
+}
+
+#deleteBtn,
+#downloadBtn {
+ margin-left: 0.6em;
+}
+#deleteBtn i {
+ color: white;
+}
+#open-delete {
+ display: inline-flex;
+}
+.custom-file-input,
+.sort-btn {
+ cursor: pointer;
+}
+
+#notification-box {
+ overflow-y: scroll;
+ max-height: 40em;
+}
+
+/* Tooltip container */
+.tooltipCustom {
+ position: relative;
+ display: inline-block;
+ /* border-bottom: 1px dotted black; If you want dots under the hoverable text */
+}
+
+/* Tooltip text */
+.tooltipCustom .tooltiptextCustom {
+ visibility: hidden;
+ background-color: black;
+ color: #fff;
+ text-align: center;
+ padding: 5px 0;
+ border-radius: 6px;
+
+ /* Position the tooltip text */
+ position: absolute;
+ z-index: 100;
+ width: 200px;
+ bottom: 100%;
+ left: 50%;
+ margin-left: -100px; /* Use half of the width (120/2 = 60), to center the tooltip */
+}
+
+/* Show the tooltip text when you mouse over the tooltip container */
+.tooltipCustom:hover .tooltiptextCustom {
+ visibility: visible;
+ z-index: 1000;
+}
+
+.notification-box {
+ padding: 10px 0px;
+ color: black;
+}
+
+#tabs,
+#content {
+ width: 100%;
+}
+
+.bg-gray {
+ background-color: #eee;
+}
+@media (max-width: 640px) {
+ #dropNot {
+ top: 50px;
+ left: -16px;
+ width: 290px;
+ }
+ .nav {
+ display: block;
+ }
+ .nav .nav-item,
+ .nav .nav-item a {
+ padding-left: 0px;
+ }
+ .message {
+ font-size: 13px;
+ }
+}
+#dropNot {
+ top: 60px;
+ left: 0px;
+ right: unset;
+ width: 460px;
+ box-shadow: 0px 5px 7px -1px #c1c1c1;
+ padding-bottom: 0px;
+ padding: 0px;
+}
+.dropdown-menu:before {
+ content: "";
+ position: absolute;
+ top: -20px;
+ left: 12px;
+ border: 10px solid #343a40;
+ border-color: transparent transparent #343a40 transparent;
+}
+
+.has-search .form-control {
+ padding-left: 2.375rem;
+}
+
+.has-search .form-control-feedback {
+ position: absolute;
+ z-index: 2;
+ display: block;
+ width: 2.375rem;
+ height: 2.375rem;
+ line-height: 2.375rem;
+ text-align: center;
+ pointer-events: none;
+ color: #aaa;
+}
+
+/* .nav-tabs {
+ display: flex;
+} */
+
+.collapse.in {
+ display: inline !important;
+}
+.p {
+ margin-bottom: 0;
+}
+
+.icon-center {
+ text-align: center!important
+}
+/* .main-container {
+ max-height: calc(100% - 199px);
+ overflow-x: hidden;
+ overflow-y: auto;
+} */
+
+.loader {
+ -webkit-animation: spin 2s linear infinite;
+ animation: spin 2s linear infinite;
+}
+
+@-webkit-keyframes spin {
+ 0% { -webkit-transform: rotate(0deg); }
+ 100% { -webkit-transform: rotate(360deg); }
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
\ No newline at end of file
diff --git a/apps/dicom-connect/table.html b/apps/dicom-connect/table.html
new file mode 100644
index 000000000..3c9a662ca
--- /dev/null
+++ b/apps/dicom-connect/table.html
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CaMicroscope Data Table
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/dicom-connect/table.js b/apps/dicom-connect/table.js
new file mode 100644
index 000000000..785cf4542
--- /dev/null
+++ b/apps/dicom-connect/table.js
@@ -0,0 +1,357 @@
+/**
+ * static variables
+ */
+
+const sources = [{
+ 'name':'j4care',
+ 'url':'https://development.j4care.com:11443/dcm4chee-arc/aets/DCM4CHEE/rs'
+
+},{
+ 'name': 'google',
+ 'url': 'https://dicomwebproxy-bqmq3usc3a-uc.a.run.app/dicomWeb'
+}]
+// const j4careStudiesUrl = 'https://development.j4care.com:11443/dcm4chee-arc/aets/DCM4CHEE/rs'
+// const dicomWebStudiesUrl = 'https://dicomwebproxy-bqmq3usc3a-uc.a.run.app/dicomWeb'
+
+/**
+ * global variables
+ */
+isAllSeriesSynced = false;
+
+const datatableConfig = {
+ scrollX: true,
+ lengthMenu: [
+ [10, 25, 50, -1],
+ [10, 25, 50, 'All']
+ ]
+}
+
+
+const page_states = {
+ sources: {
+ data: [{
+ 'name':'j4care',
+ 'url':'https://development.j4care.com:11443/dcm4chee-arc/aets/DCM4CHEE/rs'
+
+ },{
+ 'name': 'google',
+ 'url': 'https://dicomwebproxy-bqmq3usc3a-uc.a.run.app/dicomWeb'
+ }],
+ },
+ studies: {
+ data: null,
+ },
+ series: {
+ data: null,
+ },
+ instances: {
+ data: null,
+ },
+ status: 'sources', // 'sources, studies, series, instsances'
+}
+var studies = []
+
+
+
+function getStudies(baseUrl) {
+ const url = `${baseUrl}/studies`
+ return fetch(url).then(resp=>resp.json());
+}
+
+function getSeries(baseUrl, studyId) {
+ const url = `${baseUrl}/studies/${studyId}/series`
+ return fetch(url).then(resp=>resp.json());
+}
+
+function getInstances(baseUrl, studyId, seriesId) {
+ const url = `${baseUrl}/studies/${studyId}/series/${seriesId}/instances`
+ return fetch(url).then(resp=>resp.json());
+}
+
+
+
+
+
+function sanitize(string) {
+ string = string || '';
+ const map = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ '\'': ''',
+ '/': '/',
+ };
+ const reg = /[&<>"'/]/ig;
+ return string.replace(reg, (match) => (map[match]));
+}
+
+
+function initialize() {
+ const params = getUrlVars();
+ console.log('params')
+ console.log(params)
+ // store
+ const store = new Store('../../data/');
+ if(params.status=='studies'&¶ms.source){
+ page_states.status = params.status;
+ }else if(params.status=='series'&¶ms.source&¶ms.studyId) { // series table
+ page_states.status = params.status;
+ } else if(params.status=='instances'&¶ms.source&¶ms.studyId&¶ms.seriesId) { // isntasnces table
+ page_states.status = params.status;
+ }
+
+
+
+ // Studies
+ // Series
+ // Instances
+ switch (page_states.status) {
+ case 'sources':
+ $('#breadcrumb').append(`Sources`);
+ function generateLink (data, type, row) {
+ return `${row.name}`;
+ }
+ datatable = $('#datatable').DataTable({
+ ... datatableConfig,
+ 'data': page_states[page_states.status].data,
+ 'columns': [
+ {data: 'name', title: 'Name', render:generateLink}
+ ]
+ });
+
+ break;
+ case 'studies':
+
+ //get source info
+ var idx = sources.findIndex(elt=>elt.name==params.source)
+ var src = sources[idx]
+
+ // create breadcrumb for studies
+ $('#breadcrumb').append(`Sources`);
+ $('#breadcrumb').append(`${src.name}`);
+ // get all studies
+
+ getStudies(src.url).then(function(data) {
+ // mapping and merge
+ data.forEach(elt=>elt.source=src.name)
+ page_states[page_states.status].data = data
+ // ${baseUrl}/studies/${studyId}/series
+ function generateLink (data, type, row) {
+ const studyId = row['0020000D']['Value'][0]
+ return `${studyId}`;
+ }
+ datatable = $('#datatable').DataTable({
+ ... datatableConfig,
+ 'data': page_states[page_states.status].data,
+ 'columns': [
+ {data: '0020000D.Value.0', title: 'Study Id', render: generateLink},
+ {data: '00100020.Value.0', title: 'Name'},
+ {data: 'source', title: 'Source'}
+ ]
+ });
+ })
+
+ break;
+ case 'series':
+ //get source info
+ var idx = sources.findIndex(elt=>elt.name==params.source)
+ var src = sources[idx]
+
+ // create breadcrumb for series
+ $('#breadcrumb').append(`Sources`);
+ $('#breadcrumb').append(`${src.name}`);
+ $('#breadcrumb').append(`${params.studyId}`);
+ // get all series
+
+
+ getSeries(src.url, params.studyId).then(function(data) {
+ // add source and study id
+ data.forEach(elt=>{
+ elt.source=src.name
+ elt.url=src.url
+ elt.studyId=params.studyId
+ elt.status='searching' // 'searching', 'unsync', 'loading', 'done'
+ })
+ page_states[page_states.status].data = data
+ function generateLink (data, type, row) {
+ const seriesId = row['0020000E']['Value'][0]
+ const modality = row['00080060']['Value'][0]
+ if (row.status !='done') return seriesId;
+ const slideId = row.slideId;
+ //if (modality=='SM')
+ return `${seriesId}`
+
+
+ // return `${seriesId}`;
+ }
+ function generateStatus (data, type, row) {
+ switch (row.status) {
+ case 'searching':
+ // return spin
+ return '
';
+ case 'unsync':
+ // return btn
+ const seriesId = row['0020000E']['Value'][0];
+ const modality = row['00080060']['Value'][0];
+ return ``; //
+ case 'loading':
+ // return downloading
+ // return '
';
+ return ``
+ case 'done':
+ // return url
+
+ return '
';
+
+ default:
+
+ return '
';
+ }
+ }
+ datatable = $('#datatable').DataTable({
+ ... datatableConfig,
+ 'data': page_states[page_states.status].data,
+ 'columns': [
+ {data: 'status', title: 'Status', render: generateStatus},
+ {data: '0020000E.Value.0', title: 'Series Id',render:generateLink },
+ {data: '00080060.Value.0', title: 'Modality'},
+ {data: 'source', title: 'Source'},
+ {data: 'studyId', title: 'study Id'}
+
+ ]
+ });
+
+ async function checkInterval() {
+ const query = {
+ 'dicom-source-url': src.url,
+ 'study': params.studyId,
+ };
+
+ const slides = await store.findSlide(null, null, params.studyId, null, query);
+ console.log(slides)
+
+ const data = datatable.data();
+
+ for (let i = 0; i < data.length; i++) {
+ const d = data[i];
+ const modality = d['00080060']['Value'][0];
+ const series = d['0020000E']['Value'][0];
+
+ if (modality === 'SM') {
+ const idx = slides.findIndex(slide => series === slide.series);
+ if (idx !== -1) {
+ d.status = slides[idx].status;
+ d.slideId = slides[idx]._id.$oid;
+ } else {
+ d.status = 'unsync';
+ }
+ }
+
+ if (modality === 'ANN') {
+ let annotationQuery = {
+ 'provenance.image.dicom-source-url': src.url,
+ 'provenance.image.dicom-study': params.studyId,
+ 'provenance.image.dicom-series': series
+ };
+
+ let annotationCount = await store.countMarks(annotationQuery);
+ console.info("Counted " + annotationCount[0].count + " mark objects for " + series);
+
+ if (annotationCount[0].count > 0) {
+ d.status = 'done';
+ d.slideId = slides[0]._id.$oid;
+ } else {
+ d.status = 'unsync';
+ }
+ }
+ }
+
+ datatable.rows().invalidate().draw();
+
+ const series = page_states[page_states.status].data;
+
+ if (series.every(s => s.status !== 'unsync' && s.status !== 'syncing')) {
+ console.log('clear');
+ clearInterval(updateSeriesStatus);
+ }
+
+ console.log('running');
+ }
+
+ // initialize
+ checkInterval()
+ // update every 10 seconds
+ var updateSeriesStatus = setInterval(checkInterval, 10000);
+ })
+ break;
+ case 'instances':
+ //get source info
+ var idx = sources.findIndex(elt=>elt.name==params.source)
+ var src = sources[idx]
+ // create breadcrumb for instances
+ const backSeriesUrl = `../dicom-connect/table.html?source=${params.source}&status=series&studyId=${params.studyId}`
+ $('#breadcrumb').append(`Sources`);
+ $('#breadcrumb').append(`${src.name}`);
+ $('#breadcrumb').append(`${params.studyId}`);
+ $('#breadcrumb').append(`${params.seriesId}`);
+
+
+
+
+
+ getInstances(src.url, params.studyId, params.seriesId).then(function(data) {
+ // add status
+ data.forEach(elt=>{
+ elt.source=params.source
+ elt.studyId=params.studyId
+ elt.seriesId=params.seriesId
+
+ })
+ page_states[page_states.status].data = data
+ function generateLink (data, type, row) {
+ const {studyId, seriesId, status}= row
+ const instanceId = row['00080018']['Value'][0]
+ if (status=='done') return instanceId
+ return `${instanceId}`;
+ }
+
+ datatable = $('#datatable').DataTable({
+ ... datatableConfig,
+ 'data': page_states[page_states.status].data,
+ 'columns': [
+ {data: '00080018.Value.0', title: 'Instance Id', render: generateLink},
+ {data: 'source', title: 'Source'},
+ {data: 'seriesId', title: 'Series Id'},
+ {data: 'studyId', title: 'Study Id'},
+
+ ]
+ });
+ })
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+$(document).ready(function() {
+ initialize();
+});
+
+
+async function syncSeries(source_url, study, series, modality) {
+ console.log(source_url, study, series, modality);
+ const result = await store.syncSeries('../../', {source_url, study, series, modality})
+ console.log('syncSeries:');
+ console.log(result);
+}
+
+function checkSeriesStatus() {
+ const series = page_states[page_states.status].data
+ series.map()
+
+}
+// table.rows.add( dataset ).draw().
+
diff --git a/apps/mini/uicallbacks.js b/apps/mini/uicallbacks.js
index e24265d19..4c37458fb 100644
--- a/apps/mini/uicallbacks.js
+++ b/apps/mini/uicallbacks.js
@@ -1359,7 +1359,8 @@ function locationCallback(layerData) {
return;
}
// locate annotation 3.0
- if (item.data.geometries.features[0].geometry.type == 'Point') {
+ const geoType = item.data.geometries.features[0].geometry.type;
+ if (geoType == 'Point'||geoType == 'Circle'||geoType == 'Ellipse') {
const bound = item.data.geometries.features[0].bound.coordinates;
const center = $CAMIC.viewer.viewport.imageToViewportCoordinates(
bound[0],
diff --git a/apps/viewer/uicallbacks.js b/apps/viewer/uicallbacks.js
index 8f2015fd0..69a7b5e8f 100644
--- a/apps/viewer/uicallbacks.js
+++ b/apps/viewer/uicallbacks.js
@@ -1547,7 +1547,8 @@ function locationCallback(layerData) {
return;
}
// locate annotation 3.0
- if (item.data.geometries.features[0].geometry.type == 'Point') {
+ const geoType = item.data.geometries.features[0].geometry.type;
+ if (geoType == 'Point'||geoType == 'Circle'||geoType == 'Ellipse') {
const bound = item.data.geometries.features[0].bound.coordinates;
const center = $CAMIC.viewer.viewport.imageToViewportCoordinates(
bound[0],
diff --git a/common/DrawHelper.js b/common/DrawHelper.js
index a72813745..c36ab6c75 100644
--- a/common/DrawHelper.js
+++ b/common/DrawHelper.js
@@ -82,7 +82,9 @@ caDrawHelper.prototype.drawMultiline = function(ctx,array){
this.drawLine(ctx,array[i-1],array[i]);
}
}
-caDrawHelper.prototype.circle = function(ctx, point, radius){
+caDrawHelper.prototype.circle = function(ctx, point, radius, isPoint=true){
+
+
const path = new Path();
path.arc(
point[0],
@@ -90,11 +92,34 @@ caDrawHelper.prototype.circle = function(ctx, point, radius){
radius, 0, 2 * Math.PI
);
path.closePath();
- path.strokeAndFill(ctx);
- //path.stroke(ctx);
+ if(isPoint) {
+ path.strokeAndFill(ctx);
+ } else {
+ path.stroke(ctx);
+ }
// return points and path
return path;
}
+
+// rotation in radians
+caDrawHelper.prototype.ellipse = function(ctx, point, radius, rotation){
+ const path = new Path();
+ path.ellipse(
+ point[0],
+ point[1],
+ radius[0],
+ radius[1],
+ rotation,
+ 0,
+ 2 * Math.PI
+ );
+ path.closePath();
+ // path.strokeAndFill(ctx);
+ path.stroke(ctx);
+ // return points and path
+ return path;
+}
+
caDrawHelper.prototype.drawMultiGrid = function(ctx, points, size){
const path = new Path();
points.forEach(p=>{
@@ -125,25 +150,6 @@ caDrawHelper.prototype.drawLine = function(ctx, start, end){
ctx.stroke();
}
-/**
- * draw a circle
- * @param {CanvasRenderingContext2D} ctx
- * is used for drawing rectangles, text, images and other objects onto the canvas element
- * @param {Number} cx
- * The x-coordinate of the center of the circle
- * @param {Number} xy
- * The x-coordinate of the center of the circle
- * @param {Number} r
- * The radius of the circle
- */
-caDrawHelper.prototype.drawCircle = function(ctx, cx, cy, r){
- // draw line
- ctx.beginPath();
- ctx.arc(cx, cy, r, 0, 2 * Math.PI);
- ctx.stroke();
- ctx.closePath()
-
-}
/**
* draw a polygon on a canvas
* @param {CanvasRenderingContext2D} ctx
@@ -165,8 +171,8 @@ caDrawHelper.prototype.drawPolygon = function(ctx, paths){
// close path and set style
path.closePath()
if(ctx.isFill ==undefined || ctx.isFill){
- // path.fill(ctx);
- path.stroke(ctx);
+ path.fill(ctx);
+ //path.stroke(ctx);
}else{
path.stroke(ctx);
}
@@ -221,8 +227,25 @@ caDrawHelper.prototype.draw = function(ctx, image_data){
&& !this.isPointInBBox(ctx.viewBoundBoxInData, {x:point[0],y:point[1]})) continue;
ctx.fillStyle = (ctx.isFill ==undefined || ctx.isFill)?hexToRgbA(style.color,1):style.color;
+ console.log(this)
polygon.geometry.path = this.circle(ctx, polygon.geometry.coordinates, ctx.radius);
- }else if(false){
+ }
+ else if(polygon.geometry.type=='Circle') {
+ const point = polygon.geometry.coordinates
+ if(ctx.viewBoundBoxInData
+ && !this.isPointInBBox(ctx.viewBoundBoxInData, {x:point[0],y:point[1]})) continue;
+
+ ctx.fillStyle = (ctx.isFill ==undefined || ctx.isFill)?hexToRgbA(style.color,1):style.color;
+ polygon.geometry.path = this.circle(ctx, polygon.geometry.coordinates, polygon.geometry.radius, false);
+ }else if(polygon.geometry.type=='Ellipse'){
+ const point = polygon.geometry.coordinates
+ if(ctx.viewBoundBoxInData
+ && !this.isPointInBBox(ctx.viewBoundBoxInData, {x:point[0],y:point[1]})) continue;
+
+ ctx.fillStyle = (ctx.isFill ==undefined || ctx.isFill)?hexToRgbA(style.color,1):style.color;
+ polygon.geometry.path = this.ellipse(ctx, polygon.geometry.coordinates, polygon.geometry.radius, polygon.geometry.rotation);
+ }
+ else if(false){
}else{
// determine drawing or not
@@ -328,5 +351,5 @@ var DrawHelper = new caDrawHelper();
//OpenSeadragon.DrawHelper = DrawHelper;
+module.exports = caDrawHelper;
-module.exports = caDrawHelper;
\ No newline at end of file
diff --git a/common/util.js b/common/util.js
index d0be60d12..9e367ae26 100644
--- a/common/util.js
+++ b/common/util.js
@@ -411,13 +411,22 @@ function VieweportFeaturesToImageFeatures(viewer, geometries) {
this.imgHeight = image.source.dimensions.y;
geometries.features = geometries.features.map((feature) => {
- if (feature.geometry.type=='Point') {
+ if (feature.geometry.type=='Point'||feature.geometry.type=='Circle'||feature.geometry.type=='Ellipse') {
feature.geometry.coordinates = [
Math.round(feature.geometry.coordinates[0] * imgWidth),
Math.round(feature.geometry.coordinates[1] * imgHeight)];
feature.bound.coordinates =[
Math.round(feature.bound.coordinates[0] * imgWidth),
Math.round(feature.bound.coordinates[1] * imgHeight)];
+
+
+
+ if (feature.geometry.type=='Circle') {
+ feature.geometry.radius = Math.round(feature.geometry.radius * imgWidth)
+ }
+ if (feature.geometry.type=='Ellipse') {
+ feature.geometry.radius = [Math.round(feature.geometry.radius[0] * imgWidth), Math.round(feature.geometry.radius[1] * imgHeight)];
+ }
return feature;
}
feature.geometry.coordinates[0] = feature.geometry.coordinates[0].map(
diff --git a/core/Store.js b/core/Store.js
index 65ed127eb..30374c993 100644
--- a/core/Store.js
+++ b/core/Store.js
@@ -36,6 +36,7 @@ function objToParamStr(obj) {
class Store {
constructor(base, validation, config) {
this.base = base || './data/';
+
this.validation = validation || {};
this.config = config;
}
@@ -154,6 +155,57 @@ class Store {
mode: 'cors',
});
}
+ findMarks(q) {
+ const suffix = 'Mark/find';
+ const url = this.base + suffix;
+ var query = {};
+ if (q) {
+ query = q;
+ }
+ return fetch(url + '?' + objToParamStr(query), {
+ credentials: 'include',
+ mode: 'cors',
+ }).then(this.errorHandler).then((x) => this.filterBroken(x, 'mark'));
+ }
+
+ countMarks(q) {
+ const suffix = 'Mark/count';
+ const url = this.base + suffix;
+ var query = {};
+ if (q) {
+ query = q;
+ }
+ return fetch(url + '?' + objToParamStr(query), {
+ credentials: 'include',
+ mode: 'cors',
+ }).then(this.errorHandler).then((x) => this.filterBroken(x, 'mark'));
+ }
+
+ findSlide(name, specimen, study, location, q) {
+ let query = {};
+ const suffix = 'Slide/find';
+ const url = this.base + suffix;
+ if (q) {
+ query = q;
+ } else {
+ if (name) {
+ query.name = name;
+ }
+ if (study) {
+ query.study = study;
+ }
+ if (specimen) {
+ query.specimen = specimen;
+ }
+ if (location) {
+ query.location = location;
+ }
+ }
+ return fetch(url + '?' + objToParamStr(query), {
+ credentials: 'include',
+ mode: 'cors',
+ }).then(this.errorHandler);
+ }
/**
* find marks matching slide and/or marktype
* will search by slide field as exactly given and by the oid slide of that name
@@ -945,7 +997,34 @@ class Store {
body: JSON.stringify(update),
});
}
-
+ /***
+ * dicom api start
+ *
+ */
+ async syncSeries(baseUrl, data = {}) {
+ // the data structure:
+ // const {source_url, study, series, modality} = data
+
+ const suffix = 'loader/dicomWeb/importSeries';
+ const url = baseUrl + suffix;
+ // Default options are marked with *
+ const response = await fetch(url, {
+ method: "POST",
+ mode: "cors",
+ cache: "no-cache",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ redirect: "follow",
+ referrerPolicy: "no-referrer",
+ body: JSON.stringify(data),
+ });
+ return response.json();
+ }
+ /***
+ * dicom api end
+ *
+ */
addPresetLabels(labels) {
const suffix = 'Presetlabels/add';
const url = this.base + suffix;
diff --git a/core/extension/openseadragon-overlays-manage.js b/core/extension/openseadragon-overlays-manage.js
index 6116e7d9b..889cb4400 100644
--- a/core/extension/openseadragon-overlays-manage.js
+++ b/core/extension/openseadragon-overlays-manage.js
@@ -383,6 +383,12 @@
pointPath.closePath();
pointPath.strokeAndFill(ctx);
this.editPointPathList.push(pointPath);
+ } else if (this.editPathData.geometry.type === 'Circle') {
+ // TODO editor
+ console.log('drawEditPoints Circle');
+ } else if (this.editPathData.geometry.type === 'Ellipse') {
+ // TODO editor
+ console.log('drawEditPoints Ellipse');
} else {
pathData[0].map((point) => {
const pointPath = new Path();
@@ -497,6 +503,12 @@
pointPath.closePath();
pointPath.strokeAndFill(this._edit_tool_ctx_);
this.editPointPathList.push(pointPath);
+ } else if (this.editPathData.geometry.type === 'Circle') {
+ // TODO editor
+ console.log('onEditPointMouseMove Circle');
+ } else if (this.editPathData.geometry.type === 'Ellipse') {
+ // TODO editor
+ console.log('onEditPointMouseMove Ellipse');
} else {
// brush
this.editPathData.geometry.coordinates[0][this.onEditIndex] = [img_point.x, img_point.y];