I am currently developing a web-based application for visualizing data. The app is fed with a json file,formatted like this:
var theData = {
"nodes":{
"IBM":{
"type":"Institution",
"properties":{
"founded_in":"1998",
"director":"whoever"
}
},
"Joseph":{
"type":"Employee",
"properties":{
"Nationality":"bangladeshi"
}
},
[...]
},
"edges":{
"Joseph-IBM":{
"labeled":"Works at"
},
[...]
}
}
NOTE: The json is not entirely.
This data is loaded into the application and it will show you:
The nodes are draggables alongside the entire screen and both the lines and the label of the relationships are moved accordingly.
What would I like to?
I would like to make the surface in which the graph "lays" movable and zoomable, so the UX can be significantly improved. The way I have seen is
by using a canvas. The problem I find, is that the canvas is the first layer and the elements that are inserted in cannot be neither hovered nor clickable (to add events, for example). I do know that those are not really problems, since there is a graph vis that uses (canvas), but I do not know how to solve it.
I am not asking you to give me the exact code (if you want, it would be much appreciated :)), but to help me in finding something to achieve a movable and zoomable surface!
I hope that everthing is clear & Thank you in advance for any help you can provide.
Related
HTML element 'Canvas' provide functions getImageData and putImageData, so it is possible to read as well as to modify image-data. I need this functionality in an ExtJS (Sencha,version 5.1) project. I have to use a sprite of type 'image'. But I can't find any functions addressing neither access to pixels nor access to undrlying canvas, if such is used. Is there an another approach to achieve this?
with regards
Rafal Ziolkowski
So what you need to do is draw your sprites onto a canvas? From a browse of the ext js code there is a Ext.draw.engine.Canvas component which I believe uses a html canvas element. In this class you may be able to do what you want and get image data. If not you can always go and grab the element it renders and call the appropriate getImageData and putImageData. Hope this helps.
drawContainer = Ext.create('Ext.draw.Container', {
items: [{
mySprite
}]
});
// after render
surface = drawContainer.getSurface();
surface.flatten(size, surface); // returns the drawn image data
// do this or get the element
canvas = surface.el.down('canvas');
not sure if this is what you want but hope it helps. Consult the documentation theres plenty of information on drawing sprites.
senchadocs
This is not a: "Do all the work for me!" kind of question. I just wanna know which approach you think would be suitable for this challenge.
I have this map:
As you can see by the blue marker, I've roughly drawned some selections/areas of the map. Theese areas I want to serve as links.
But I don't quite know how to grasp this challenge, since all of the areas have quite odd shapes.
I have looked at cords, but it seems like a huge job with all of the twists and turns that I would need to do.
I would be awesome if I could just slice up the areas in Photoshop and save each of them as .png and just tell my page to ignore the transparent area! But that's just wishfull thinking I suppose.
I hope that one of you have a suggestion that I've overlooked.
Give a try to these -
http://polymaps.org/
http://www.amcharts.com/javascript-maps/
Raphael JS
You can try making an SVG version of your map and then implement it's clickiness with one of these libraries depending on which one you choose.
Here's one tutorial to do this with Raphael JS - http://parall.ax/blog/view/2985/tutorial-creating-an-interactive-svg-map
Make an image for each clickeable zone, like this:
Register to the click event of the img element from the page, this way:
var getAreaFromXY = function(x,y) {
// for each section colored map
// get pixel color on x,y (see http://stackoverflow.com/questions/8751020/how-to-get-a-pixels-x-y-coordinate-color-from-an-image)
// if the color is red, that is the zone
};
$(".post-text img").click(function(e) {
var area = getAreaFromXY(e.offsetX, e.offsetY);
});
I have implemented two dygraphs with custom option verticalCrosshair : true here:
https://rawgit.com/danielkrizian/dygraphs/master/tests/synchronize-Crosshair.html
When I hover over any of the graphs at a particular x point, I want all graphs to display the vertical crosshair at that point.
So far I've been able to get this working on the top graph (gs[0]) like this:
highlightCallback: function(e, x, pts, row) {
var sel = gs[0].getSelection();
gs[1].setSelection(sel);
},
Nothing happens when I hover over the bottom graph. How to generalize it with for loop over all graphs?
You should put the dygraphs objects in an array and loop over it in your highlightCallback, updating the selection in all the dygraphs other than the one generating the event.
One complication is that highlightCallback doesn't get the dygraph object as a parameter. This is an oversight in the API which I hope to fix in dygraphs 2.0. You can work around it by capturing the relevant Dygraph object in a closure when you set highlightCallback.
See the synchronize demo for some inspiration.
I've solved this with:
highlightCallback: function(e, x, pts, row) {
for (var j = 0; j < gs.length; j++) {
gs[j].setSelection(row);
}
},
See the graphs and source at:
https://rawgit.com/danielkrizian/dygraphs/master/tests/synchronize-Crosshair.html
I have had both of these features up and working for nearly a year. I didn't originate the code, but just did just some editing in an effort to get the labelFollow bit working the way that I liked. I followed the original author(s) and stuffed the code in the library, not in a mere callback in a Dygraph() options argument, but I did not put the verticalCrosshair snippets in a plug-in as I don't know how to write those yet. However, the labelFollow stuff is in the plug-in legend.js, as that's what the original author had done.
As for the code for the verticalCrosshair option, I got it from the apparently esteemable DJCOMXA--- http://www.pixeltradr.com/dygraphs/dygraph.js. Just search for "verticalCrosshair" and you'll find where two tiny snippets have been added to that script.
To complete the verticalCrosshair option it's necessary of course to also add to dygraph-options-reference.js as follows:
// Credit due to DJCOMXA.
"verticalCrosshair": {
"default": "false",
"labels": ["Interactive Elements"],
"type": "boolean",
"description": "Shows vertical line on highlighted point."
},
Now for the other matter of the labelFollow, for proof of prior work on labelFollow it suffices to go to the Google group. You can then click on the link at the very bottom of that page to see a graph that shows both the verticalCrosshair and the labelFollow (the latter being, I believe, the work of "wootwoot" who I more or less copied). Similarly, replace "synchronize-Charts" in that URL for that graph with "customLabel_Crosshair" and you'll be taken to a prettier example (I think that I'm limited as to the number of links that I can provide, hence the URL re-construction instructions).
I can't now figure out how I got the original labelFollow code. Whatever, I found some modifications to legend.js (and of course dygraph-options-reference.js) by wootwoot and edited them.
I must say that I wrote to danvdk at the gmail address that he provided at the top of dygraph.js, to suggest the utility of these changes, and it was bounced as I was not a member of the club.
Regarding taking inspiration from the Dygraphs sychronization example (the "synchronize demo" link that danvk provided), take your inspiration from it cautiously. Right now it's giving me fits.
Try this: upon page load go to any of the four graphs and in the middle at a skinny section do a zoom in (click drag from left to right); then double-click.
What happened? You zoomed in and the traces filled the graph vertically, but for a little bit of padding--- automatic scaling. And then upon zooming out with the double-click everything appeared to be as before. Ahh... but it isn't. Now move to any OTHER of the four graphs and repeat the first step... zoom in at a the same skinny spot (the data happen to be the same for each of these graphs). Notice that the automatic vertical scaling is missing. And that's a permanent condition until you reload the page.
I want to split my map into tiles/territories. So i've prepared another layer showing squares. But this layer is full of .png image files so there is no data/object for this squares.
I've also tried to draw squares with leaflet's geometry objects. But it causing performance issues, there is times to show 500+ squares.
If you develop something like that what method would you prefer? UTFGrid? GeoJSON/Geometry? Or maybe any other better solution?
UPDATE:
Actually i don't want to get data belongs to square's territory i just want to change the square's color somehow i mean somehow i want to highlight that area maybe i can create a rectangle on the fly when user mouseover.
And im trying avoid to use UTFGrid for just highlighting. But I want to ensure the UTFGrid is the only way or not.
This sounds like the exact reason that UTFGrid was created! This site links to the tutorial that I used when learning UTFGrid, and it is solid.
Updated after your update:
MarkerCluster might have the look/feel you are going after, they basically paint a polygon onto the map layer. You can check the source here, and here's a relevant snippet:
_showCoverage: function (e) {
var map = this._map;
if (this._inZoomAnimation) {
return;
}
if (this._shownPolygon) {
map.removeLayer(this._shownPolygon);
}
if (e.layer.getChildCount() > 2 && e.layer !== this._spiderfied) {
this._shownPolygon = new L.Polygon(e.layer.getConvexHull(), this.options.polygonOptions);
map.addLayer(this._shownPolygon);
}
},
I'm working on an application that uses Raphael to draw primitive shapes (rectangles, ellipses, triangles etc) and lines but allows the user to move/resize these objects as well. One of the main requirements is that the face of shapes can have formatted text. The actual text is a subset of Markdown (simple things like bolding, italics, lists) and is rendered as HTML.
FWIW - I'm using Backbone.js views to modularize the shape logic.
Approach 1
My initial thought was to use a combination of foreignObject for SVG and direct HTML with VML for IE. However, IE9 doesn't support foreignObject, and therefore this approach had to be abandoned.
Approach 2
With the beside the canvas object, add divs that contain the actual HTML. Then, position them over the actual shape with a transparent background. I've created a shape view that has references to both the actual Raphael shape and the "overlay" div. There are a couple of problems with this approach:
Using overlay that aren't children of the SVG/VML container feels wrong. Does having this overlay element cause other issues with rendering down the road?
Events that are normally trapped by Raphael (drag, click etc) need to be forwarded from the overlay to the Raphael object. For browsers that support pointer-events, this is easily done:
div.shape-text-overlay {
position: absolute;
background: none;
pointer-events: none;
}
However, other browsers (like IE8 and below) need forwarding of the events:
var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout';
this.$elText.on(forwardedEvents, function(e) {
var original = e.originalEvent;
var event;
if (document.createEvent) {
event = document.createEvent('HTMLEvents');
event.initEvent(e.type, true, true);
}
else {
event = document.createEventObject();
event.eventType = e.type;
}
// FYI - This is the most simplistic approach to event forwarding.
// My real implementation is much larger and uses MouseEvents and UIEvents separately.
event.eventName = e.type;
_.extend(event, original);
if (document.createEvent) {
that.el.node.dispatchEvent(event);
}
else {
that.el.node.fireEvent('on' + event.eventType, event);
}
});
Overlapping shapes cause the text to be overlapped because the text/shapes are on different layers. Although overlapping shapes won't be common, this looks bad:
This approach is what I'm currently using but it just feels like a huge hack.
Approach 3
Almost like Approach 1, but this would involve writing text nodes/VML nodes directly. The biggest problem with this is the amount of manual conversion necessary. Breaking outside of Raphael's API seems like it could cause stability issues with the other Raphael objects.
Question
Has anyone else had to do something similar (rendering HTML inside of SVG/VML)? If so, how did you solve this problem? Were events taken into account?
I built this project (no longer live) using Raphael. What I did is actually abandoned the idea of using HTML inside of SVG because it was just too messy. Instead, I absolutely positioned an HTML layer on top of the SVG layer and moved them around together. When I wanted the HTML to show, I merely faded it in and faded the corresponding SVG object out. If timed and lined up correctly, it's not really noticeable to the untrained eye.
While this may not necessarily be what you're looking for, maybe it will get your mind thinking of new ways to approach your problem! Feel free to look at the JS on that page, as it is unminified ;)
PS, the project is a lead-gathering application. If you just want to see how it works, select "Friend" in the first dropdown and you don't have to provide any info.
Unless another answer can be provided and trump my solution, I have continued with the extra div layer. Forwarding events was the most difficult part (if anyone requires the forwarding events code, I can post it here). Again, the largest downside to this solution is that overlapping shapes will cause their text to overlap above the actual drawn shape.