Add Annotation to Openseadragon - javascript

var viewer = OpenSeadragon({
id: "openseadragon1",
prefixUrl: "images/openseadragon/",
showNavigator: true,
navigatorPosition: "BOTTOM_RIGHT",
tileSources: '/fcgi-bin/iipsrv.fcgi?Deepzoom=<?=$plink?>.jp2.dzi',
crossOriginPolicy: 'Anonymous',
zoomInButton: "zoom-in",
zoomOutButton: "zoom-out",
homeButton: "home",
fullPageButton: "full-page"
});
anno.makeAnnotatable(viewer);
$.ajax({
url: "handlers/H_AnnotationHandler.php",
data: "case_id=<?=$case_id?>&plink=<?=$plink?>&mode=get",
type: "post",
dataType: "json",
success: function(response) {
if (!response.error) {
for (var i=0; i<response.annots.length; i++) {
console.log(response.annots[i].comment);
anno.addAnnotation({
text: response.annots[i].comment,
shapes: [{
type: 'rect',
geometry: {
x: response.annots[i].rect_x,
y: response.annots[i].rect_y,
width: response.annots[i].rect_w,
height: response.annots[i].rect_h
}
}]
});
}
} else {
console.log(response.error);
}
}
});
I can add annotation live : http://annotorious.github.io/demos/openseadragon-preview.html
After user added the annotation, I store in my database. When the user refresh the page, I am loading saved datas from database using ajax call (H_AnnotationHandler.php). Returning data is true, but I could not draw annotation on jpeg2000 image using anno.addAnnotation, how can I draw it ?
Reference : Add annotations API.

You are missing src attribute in anno.addAnnotation method, the thing is that I don't really know what value should go there, there is nothing in documentation about that, the only thing I could find in internet is this plugin:
https://github.com/dgutman/OpenSeadragon-Plugins/tree/master/annotationState_Code
You could try that instead.
EDIT
I actually managed to attach event programatically on the demo page, the open sea dragon module there is registered as a dzi://openseadragon/something, knowing this you can invoke the function
anno.addAnnotation({
src : 'dzi://openseadragon/something',
text : 'My annotation',
shapes : [{
type : 'rect',
geometry : { x : 0.8, y: 0.8, width : 0.2, height: 0.2 }
}]
});
from console (or in your code within the ajax-success loop) and it will be added to the image. Yet, the naming method is pretty... well, I found this in the source code:
annotorious.mediatypes.openseadragon.OpenSeadragonModule.prototype.getItemURL = function(item) {
// TODO implement something decent!
return 'dzi://openseadragon/something';
}
so be assured that this might change in future.

Since you didn't share your code I tried to set it up here: https://jsfiddle.net/peLokacs/3/, please do the necessary changes so I can see the annotations. Also I get a "Annotorious does not support this media type in the current version or build configuration." error, If you managed to save the annotations, then you probably know how to fix that too.
var viewer = OpenSeadragon({
id: "openseadragon-1",
prefixUrl: "https://neswork.com/javascript/openseadragon-bin-2.1.0/images/",
//tileSources: "https://openseadragon.github.io/example-images/highsmith/highsmith.dzi",
tileSources: { type: 'legacy-image-pyramid', levels: [{ url: '0001q.jpg', height: 889, width: 600 }, { url: '0001r.jpg', height: 2201, width: 1485 }, { url: '0001v.jpg', height: 4402, width: 2970 }]},
showNavigator: true,
});

Related

Data driven styling errors using client-side join - Mapbox GL JS

I have implemented a client-side join from a GitHub based CSV to a Mapbox tileset using a Papa-parse promise function, similar to how it is implemented in: Data Joins : Mapbox JS.
The promise is fulfilled and the data is stored correctly, the outlines of the regions I am trying to visualise show, but there is an issue with data-driven styling with the "interpolate", ['linear'] parameters I am trying to use, from my past experience. The 'accessibilityOutline' ID layer shows its data correctly, so it is confusing to why this is happening.
An error keeps coming up declaring
"Input/output pairs for "interpolate" expressions must be defined using literal numeric values (not computed expressions) for the input values."
I am wondering if anyone else has had this problem, and the way it was overcome. Any help would be grateful.
The code implemented is below, this is all based off the link above. The data is read in correctly and through debug testing I can see that all the data is in the correct format as the
dynamicTyping: true,
is present in the Papa.parse function. However, mapbox looks like it is unable to read this data.
function papaPromise(url) {
return new Promise(function(resolve,reject) {
Papa.parse(url, {
download: true,
header: true,
skipEmptyLines: true,
complete: resolve,
dynamicTyping: true,
});
});
}
const accessibilityCSV = papaPromise("some random url of data");
const mapContainer = useRef();
useEffect(() => {
const map = new mapboxgl.Map({
container: mapContainer.current,
style: "mapbox://styles/mapbox/outdoors-v11",
center: [-2.597, 53.39],
zoom: 9.5,
});
map.on("load", () => {
accessibilityCSV.then(function (results) {
console.log(results.data);
results.data.forEach((row) => {
map.setFeatureState({
source: 'lsoa_ultra_generalised-3gznbd',
sourceLayer: 'lsoa_ultra_generalised-3gznbd',
id: row.lsoa_code
}, {
lsoa_name: row.lsoa_name,
Total_LSOA_population: row.Total_LSOA_population,
LSOA_population_low_income: row.LSOA_population_low_income,
airport_jt60: row.airport_jt60,
airport_jt90: row.airport_jt90,
seaport_jt60: row.seaport_jt60,
seaport_jt90: row.seaport_jt90,
city_jt60: row.city_jt60,
city_jt90: row.city_jt90,
visitor_attraction_jt60: row.visitor_attraction_jt60,
visitor_attraction_jt90: row.visitor_attraction_jt90,
beach_jt60: row.beach_jt60,
beach_jt90: row.beach_jt90,
national_park_jt60: row.national_park_jt60,
national_park_jt90: row.national_park_jt90,
biz_60: row.biz_60,
biz_90: row.biz_90,
NPIER_60: row.nPIER_60,
NPIER_90: row.nPIER_90,
uni_places_60: row.uni_places_60,
uni_places_90: row.uni_places_90
},
);
});
});
map.addSource('northOutline', {
type: "vector",
url: "vectorURL"
})
map.addSource("accessibilitySource", {
type: "vector",
url: "vectorURL",
promoteId: 'LSOA11CD'
});
map.addLayer({
id: 'accessibility',
type: 'fill',
source: 'accessibilitySource',
'source-layer': 'lsoa_ultra_generalised-3gznbd',
paint: {
"fill-color":[
"interpolate",
['linear'],
['number', ["feature-state","Total_LSOA_population"]],
0,
"#fee5d9",
2075,
"#fcae91",
4150,
"#fb6a4a",
6225,
"#de2d26",
8300,
"#a50f15",
/* other */, "#ccc",
],
},
});
map.addLayer({
id:'accessibilityOutline',
type:'line',
source: 'accessibilitySource',
'source-layer': 'lsoa_ultra_generalised-3gznbd',
paint: {
"line-color": '#000000'
},
});

Ext.Defer gives getAsynchronousLoad Error

I've just defined a combobox. Firstly it loads a countrylist and when select a value it's fire a change event which doing a ajax query to DB within searching service;
The thing; this configuration works pretty well when I click and open combobox items. But when I'm typing to combobox's field it's fires listener's store.load and because of none of country selected yet, the search query url gives not found errors of course.
{
xtype: 'countrycombo',
itemId: 'countryName',
name:'country',
afterLabelTextTpl: MyApp.Globals.required,
allowBlank: false,
flex: 1,
// forceSelection: false,
// typeAhead: true,
// typeAheadDelay: 50,
store: {
proxy: {
type: 'ajax',
// isSynchronous: true,
url: MyApp.Globals.getUrl() + '/country/list?limit=250',
// timeout: 300000,
reader: {
type: 'json',
rootProperty: 'data'
}
},
pageSize: 0,
sorters: 'description',
autoLoad: true
}
,
listeners: {
change: function (combo, countryId) {
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy()
.setUrl(MyAppp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
// Ext.defer(cityStore.load, 100);
cityStore.load();
}
}
},
I've tried several things as you see in code above to set a delay/timeout for load during typing to combobox text field; Ext.defer, timeoutconfig on proxy, typeAhead config on combo but none of them worked!
I thought that Ext.defer is the best solution but it gives this error:
Uncaught TypeError: me.getAsynchronousLoad is not a function at load (ProxyStore.js?_dc=15169)
How can I set a delay/timeout to combobox to fires load function?
Instead of Ext.defer(cityStore.load, 100);
try using this :
Ext.defer(function(){
cityStore.load
}, 300);
If this doest work, try increasing your delay
or you can put a logic before loading
like this :
if(countryId.length == 5){
cityStore.load
}
This will ensure that you Entered the right values before loading
Hope this helps, and Goodluck on your project
well.. I've tried to implement #Leroy's advice but somehow Ext.defer did not fire cityStore.load. So I keep examine similar situations on google and found Ext.util.DelayedTask
So configured the listerens's change to this and it's works pretty well;
listeners: {
change: function (combo, countryId) {
var alert = new Ext.util.DelayedTask(function () {
Ext.Msg.alert('Info!', 'Please select a country');
});
var cityStore = Ext.getStore('cityCombo');
cityStore.getProxy().setUrl(MyApp.Globals.getUrl() + '/city/view/search?query=countryid:'+ countryId);
if (typeof countryId === 'number') {
cityStore.load();
} else {
alert.delay(8000);
}
}
}

Using Angular.JS to bind a JSON value to a Syncfusion Circular gauge

I am working on a project using Syncfusion Javascript gauge control to display a weekly pay bonus. The data is stored on a SharePoint list. I wrote a javascript to convert the sharepoint list from XML to JSON.
<script type="text/javascript">
$ajax({
url: "/_api/web/lists/GetByTitle('bonusEntry')/items?$orderby=Date desc&$filter=Department eq 'Meltshop'&$top=1",
type: "GET",
headers: {
"accept":"application/json;odata=verbose",
},
success: function (data) {
var newMsBonus = "";
for(i=0; i < data.d.results.length; i++){
newMsBonus = newMsBonus + "<div>" + data.d.results[i].ACrew + "</div>";
}
$('#oDataanalysisScoreBoard').html(newMsBonus);
},
error: function (error) {
alert("error: " + JSON.stringify(error));
}
})
Then the value is placed in this Div.
<div id="oDataanalysisScoreBoard"></div>
Basically what I would like to do is bind the data to the Syncfusion control which is set up like this:
$("#CircularGauge1").ejCircularGauge({
width: 500,
height: 500,
backgroundColor: "#3D3F3D",
readOnly: false,
scales: [{
ticks: [{
type: "major",
distanceFromScale: 70,
height: 20,
width: 3,
color: "#ffffff"
}, {
type: "minor",
height: 12,
width: 1,
distanceFromScale: 70,
color: "#ffffff"
}],
}]
});
Then the gauge is created inside this:
<div id="CircularGauge1"></div>
The gauge will build but I cannot get the gauge to recieve the value.
If anyone has any ideas on how I can make this work or things I'm doing I would greatly appreciate any input! Thanks everyone!
EDIT:
The synfusion software creates a gauge and changes the needle based on a number value thats given to it. My ajax call pulls a number entered into a Sharepoint list and then displays that in a div.
In the above code snippet you mentioned the passing value as “String”. If you pass the string value to the loop it will concatenate as string value only. But we need to pass the integer value to the Circular Gauge properties(width, height, distancefromscale) to take effect. Hence, change the code snippet with the following.
$.ajax({
url: "/Gauge/GetData",
type: "POST",
success: function (data) {
var newMsBonus = 0;
for (i = 0; i < data.length; i++) {
newMsBonus = newMsBonus + data[i].MajorDistanceFromScale; // Here i have used the MajorScale distanceFromScale value for the demo
}
$('#oDataanalysisScoreBoard').html(newMsBonus);
},
failure: function (error) {
alert("no data available");
}
});
And we have prepared the sample to meet your requirement with the MVC application including the “.mdf” database. We have created the table called “GaugeData” and added the two record. And using the “$.ajax” called the action method “GetData” and received the “JSON” data from the controller. Refer the following code snippet.
View Page:
$.ajax({
url: "/Gauge/GetData",
type: "POST",
success: function (data) {},
failure: function (error) {
}
});
Controller Page:
public class GaugeController : Controller
{
GaugeDataDataContext db = new GaugeDataDataContext();
public JsonResult GetData()
{
IEnumerable data = db.GaugeDatas.Take(500);
return Json(data, JsonRequestBehavior.AllowGet);
}
}
And then assigned the calculated value to the gauge property. Here, I have used the “MajorDistanceFromScale” value read from the database record and assigned to the gauge properties. Refer the following coding snippet.
var distanceValue = parseInt($("#oDataanalysisScoreBoard")[0].innerHTML);
$("#CircularGauge1").ejCircularGauge({
width: 500,
height: 500,
backgroundColor: "#3D3F3D",
readOnly: false,
scales: [{
ticks: [{
type: "major",
distanceFromScale: distanceValue,
height: 20,
width: 3,
color: "#ffffff"
}, {
type: "minor",
height: 12,
width: 1,
distanceFromScale: 70,
color: "#ffffff"
}],
}]
});
And also please refer the below attached sample for more reference.
GaugeListSample

Javascript page re-route after d3.js event

I experimenting with d3.js and how to implement the framework.
At this stage of experimental implementation, I would like to make the filled Countries create alerts when I click them.
The ultimate goal is to change the alert to a page re-route, using the country name to decide which page it gets routed to.
In depth explanations are greatly appreciated.
Here is the code:
var map = new Datamap({
element: document.getElementById('container'),
fills: {
PARTS:'green',
defaultFill: 'black'
},
data: {
USA: {
fillKey: 'PARTS'
},
IRL: {
fillKey: 'PARTS',
}
},
done: function(datamap) {
datamap.svg.selectAll('.datamaps-subunit').on('click', function(geography, data) {
if(data.fillKey=='PARTS'){
alert(geography.properties.name);
}
});
}
});

ExtJS 4 TreePanel autoload

I have an Ext.tree.Panel which is has a TreeStore. This tree is in a tab. The problem is that when my application loads all of the trees used in the application load their data, even though the store is on autoLoad: false.
How could I prevent autoloading on the tree?
Ext.define('...', {
extend: 'Ext.container.Container',
alias: 'widget.listcontainer',
layout: {
type: 'vbox',
align: 'stretch'
},
items: [{
xtype: 'container',
html: "...",
border: 0
}, {
xtype: '...',
flex: 1,
bodyPadding: 5,
margin: '9 0 0 0'
}]
});
Ext.define('...', {
extend: 'Ext.data.TreeStore',
model: '...',
proxy: {
type: 'ajax',
reader: {
type: 'json',
root: 'data'
},
api: {
read: 'some url'
}
}
});
Ext.define('...', {
extend: 'Ext.tree.Panel',
alias: 'widget....',
id: '...',
title: '...',
height: 400,
collapsible: true,
useArrows: true,
rootVisible: false,
multiSelect: true,
singleExpand: true,
autoScroll: true,
store: '...',
columns: [...]
});
P.S. I've found out if I change rootVisible to true on the tree this problem doesn't happen, but then it shows to root node(which I don't want).
I hit the same problem, and to avoid an implicit request, I specified a root inline in the TreeStore's configuration, like:
Ext.create('Ext.data.TreeStore', {
model: '...',
proxy: {
type: 'ajax',
reader: {
type: 'json',
root: 'data'
},
api: {
read : 'some url'
}
folderSort: true,
rootVisible: false,
root: {expanded: true, text: "", "data": []} // <- Inline root
});
After an explicit .load the inline root is overwritten.
If root is invisible then AJAX tree will automatically load first level of hierarchy (as you already proved by yourself).
I think the best way is to make root visible or create tree after some actions. I wrote code that prevent AJAX request that loads data:
var preventTreeLoad = true;
store.on('beforeexpand', function(node) {
if (node == this.getRootNode() && preventTreeLoad) {
Ext.Ajax.abort(this.proxy.activeRequest);
delete this.proxy.activeRequest;
}
}, store);
var b = Ext.create('Ext.Button', {
text: 'Click me',
renderTo: 'btn',
});
b.on('click', function() {
preventTreeLoad = false;
this.load();
}, store);
But I'm not recommended to use this approach. For example, if javascript wasn't so fast - Ajax request may be send (response will not be read but server will execute operation).
You can put a dummy proxy in place when defining the tree, then set the real proxy when you want to begin using the tree/store. For example:
var store = Ext.define('Ext.data.TreeStore', {
...
// dummy proxy to avoid autoLoad on tree store construction
proxy: {
type: 'ajax',
url: ''
},
...
);
Then, when you want to use it for the first time,
store.setProxy({
type: 'ajax',
url: 'http://some/real/url',
...
});
store.load();
You can solve it with a small override:
Ext.override(Ext.tree.View,
{
setRootNode: function(node)
{
var me = this;
me.store.setNode(node);
me.node = node;
if (!me.rootVisible && me.store.autoLoad)
{
node.expand();
}
}
});
afterlayout you need a load()
Adding to what XenoN said (though many years later when I hit the same issue)
If the expanded property is set to true in the store definition, it will auto load even if autoLoad is set to false. this is unique to a TreeStore.
However, if we do want the store to load and expand we need to
Set expanded = true sometimes in code after creation (when we want) this also fires the loading of the previously created store.
setting store.setRoot({expanded:true}); within the consumer of the store which is Ext.tree.Panel.
This will load the store when you want it to load it.
seems like after that, store.load() is redundant since the expanded = true makes the store's proxy to load up and go to the server. weird, I know.
Simplest way is setting Store's root property
Ext.create('Ext.data.TreeStore', {
....
autoLoad:false,
root: {
expanded: false
}
});
Try with children and autoLoad : false :
root: {
children : []
}

Categories