Commit 38149ce95009363b120a01e064e1feeff27280aa
Merge branch 'master' of http://service.routetopa.eu:7480/WebCompDev/COMPONENTS
Showing
5 changed files
with
480 additions
and
0 deletions
datalets/datasetexplorer-datalet/datasetexplorer-datalet.html
0 → 100644
1 | +<!-- | |
2 | +@license | |
3 | + The MIT License (MIT) | |
4 | + | |
5 | + Copyright (c) 2015 Dipartimento di Informatica - Universit� di Salerno - Italy | |
6 | + | |
7 | + Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | + of this software and associated documentation files (the "Software"), to deal | |
9 | + in the Software without restriction, including without limitation the rights | |
10 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | + copies of the Software, and to permit persons to whom the Software is | |
12 | + furnished to do so, subject to the following conditions: | |
13 | + | |
14 | + The above copyright notice and this permission notice shall be included in | |
15 | + all copies or substantial portions of the Software. | |
16 | + | |
17 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
20 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | + THE SOFTWARE. | |
24 | +--> | |
25 | + | |
26 | +<!-- | |
27 | +* Developed by : | |
28 | +* ROUTE-TO-PA Project - grant No 645860. - www.routetopa.eu | |
29 | +* | |
30 | +--> | |
31 | + | |
32 | +<link rel="import" href="http://deep.routetopa.eu/COMPONENTS/datalets/base-ajax-json-jsonpath-datalet/base-ajax-json-jsonpath-datalet.html"> | |
33 | + | |
34 | +<!-- | |
35 | + | |
36 | +`datasetexplorer-datalet` is a treemap datalet based on d3js treemap project http://bl.ocks.org/mbostock/4063582 | |
37 | +A treemap recursively subdivides area into rectangles; the area of any node in the tree corresponds to its value. | |
38 | +This is an enhanced version designed to help users to navigate associated dataset providers. | |
39 | + | |
40 | +At this moment it requires server-side elaboration, but later version will hopefully be completely client-side. | |
41 | + | |
42 | +Example: | |
43 | + | |
44 | + <datasetexplorer-datalet | |
45 | + data-url="http://ckan.routetopa.eu/api/action/datastore_search?resource_id=#" | |
46 | + fields='["field1","field2"]'> | |
47 | + </datasetexplorer-datalet> | |
48 | + | |
49 | +@element datasetexplorer-datalet | |
50 | +@status v0.1 | |
51 | +@demo demo/index.html | |
52 | +@group datalets | |
53 | +--> | |
54 | + | |
55 | +<dom-module id="datasetexplorer-datalet"> | |
56 | + <template> | |
57 | + <style is="custom-style"> | |
58 | + | |
59 | + :host ::content h6 { | |
60 | + color: red; | |
61 | + } | |
62 | + | |
63 | + :host ::content #treemap_placeholder { | |
64 | + width: 100%; | |
65 | + height: 70%; | |
66 | + min-height: 500px; | |
67 | + background: #ddd; | |
68 | + } | |
69 | + | |
70 | + :host ::content text { | |
71 | + pointer-events: none; | |
72 | + } | |
73 | + | |
74 | + :host ::content .grandparent text { | |
75 | + font-weight: bold; | |
76 | + } | |
77 | + | |
78 | + :host ::content rect { | |
79 | + fill: none; | |
80 | + stroke: #fff; | |
81 | + } | |
82 | + | |
83 | + :host ::content rect.parent, | |
84 | + .grandparent rect { | |
85 | + stroke-width: 2px; | |
86 | + } | |
87 | + | |
88 | + :host ::content .grandparent rect { | |
89 | + fill: orange; | |
90 | + } | |
91 | + | |
92 | + :host ::content .grandparent:hover rect { | |
93 | + fill: #ee9700; | |
94 | + } | |
95 | + | |
96 | + :host ::content .children rect.parent, | |
97 | + .grandparent rect { | |
98 | + cursor: pointer; | |
99 | + } | |
100 | + | |
101 | + :host ::content .children rect.parent { | |
102 | + fill: #bbb; | |
103 | + -fill-opacity: .5; | |
104 | + fill-opacity: 1; | |
105 | + } | |
106 | + | |
107 | + :host ::content .children:hover rect.child { | |
108 | + fill: #bbb; | |
109 | + } | |
110 | + </style> | |
111 | + <div id="treemap_placeholder"></div> | |
112 | + <base-ajax-json-jsonpath-datalet data-url="{{dataUrl}}" fields="{{fields}}" data="{{data}}"></base-ajax-json-jsonpath-datalet> | |
113 | + </template> | |
114 | + | |
115 | + <script src="http://deep.routetopa.eu/COMPONENTS/datalets/shared_js/d3.js"></script> | |
116 | + <script src="js/buildtreemap.js"></script> | |
117 | + <script> | |
118 | + | |
119 | + var DatasetexplorerBehavior = { | |
120 | + | |
121 | + map : { | |
122 | + name : "", | |
123 | + children : [] | |
124 | + }, | |
125 | + | |
126 | + transformData: function(e) | |
127 | + { | |
128 | + var treemapData = []; | |
129 | + DatasetexplorerBehavior.map.name = this._component.name; | |
130 | + | |
131 | + if(this.data.length > 1) { | |
132 | + for (i = 0; i < this.data.length; i++) { | |
133 | + var propName = this.data[i].name; | |
134 | + | |
135 | + for (var j = 0; j < this.data[i].data.length; j++) { | |
136 | + if (i == 0) treemapData[j] = {}; | |
137 | + currObj = {}; | |
138 | + currObj[propName] = this.data[i].data[j]; | |
139 | + jQuery.extend(treemapData[j], currObj); | |
140 | + } | |
141 | + } | |
142 | + }else{ | |
143 | + treemapData = this.data[0].data; | |
144 | + } | |
145 | + | |
146 | + this.map.children = []; | |
147 | + | |
148 | + for(var i = 0; i < treemapData.length; i++){ | |
149 | + this.checkAggragationField(treemapData[i], this._component.fields.length - 1, this._component.fields.length - 1); | |
150 | + } | |
151 | + | |
152 | + var json = JSON.stringify(this.map); | |
153 | + | |
154 | + }, | |
155 | + | |
156 | + findChild: function(child, category){ | |
157 | + var children = child.children; | |
158 | + for (var i=0; i<children.length; i++) { | |
159 | + if (children[i].name == category) | |
160 | + return children[i]; | |
161 | + } | |
162 | + var nchild = {name : category , children : []}; | |
163 | + children.push(nchild); | |
164 | + return nchild; | |
165 | + }, | |
166 | + | |
167 | + checkAggragationField: function(object, levels, value_index){ | |
168 | + var curchild = this.map; | |
169 | + var keys = Object.keys(object); | |
170 | + for(var level= 0; level < levels; level++){ | |
171 | + | |
172 | + var child = this.findChild(curchild, object[keys[level]]); | |
173 | + curchild = child; | |
174 | + } | |
175 | + | |
176 | + if (curchild.value === undefined) | |
177 | + curchild.value = 0; | |
178 | + | |
179 | + var value = curchild.value + parseFloat(object[keys[value_index]]); | |
180 | + curchild.children = null; | |
181 | + curchild.value = value; | |
182 | + }, | |
183 | + | |
184 | + presentData: function(){ | |
185 | + build(this.map, "treemap_placeholder"); | |
186 | + } | |
187 | + }; | |
188 | + | |
189 | + | |
190 | + Polymer({ | |
191 | + is : 'datasetexplorer-datalet' , | |
192 | + | |
193 | + properties: { | |
194 | + /** | |
195 | + * It's the name for treemap | |
196 | + * | |
197 | + * @attribute name | |
198 | + * @type String | |
199 | + * @default '' | |
200 | + */ | |
201 | + name: { | |
202 | + type: String, | |
203 | + value: "" | |
204 | + }, | |
205 | + /** | |
206 | + * It's the component behavior | |
207 | + * | |
208 | + * @attribute behavior | |
209 | + * @type Object | |
210 | + * @default {} | |
211 | + */ | |
212 | + behavior : { | |
213 | + type : Object, | |
214 | + value : {} | |
215 | + } | |
216 | + }, | |
217 | + | |
218 | + ready: function(){ | |
219 | + this.behavior = $.extend(true, {}, BaseDataletBehavior, WorkcycleBehavior, AjaxJsonJsonPathBehavior, DatasetexplorerBehavior); | |
220 | + this.async(function(){this.behavior.init(this)},1000); | |
221 | + } | |
222 | + | |
223 | + }); | |
224 | + </script> | |
225 | +</dom-module> | |
0 | 226 | \ No newline at end of file | ... | ... |
datalets/datasetexplorer-datalet/datasetexplorer-datalet.png
0 → 100644
1.58 KB
datalets/datasetexplorer-datalet/demo/index.html
0 → 100644
1 | +<!DOCTYPE html> | |
2 | +<html lang="en"> | |
3 | +<head> | |
4 | + <meta charset="UTF-8"> | |
5 | + <title></title> | |
6 | + | |
7 | + <script> | |
8 | + </script> | |
9 | + | |
10 | +</head> | |
11 | +<body> | |
12 | + | |
13 | +<script src="https://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript"></script> | |
14 | +<link rel="import" href="../datasetexplorer-datalet.html" /> | |
15 | + | |
16 | +<datasetexplorer-datalet | |
17 | + data-url="http://spod.routetopa.eu/openwall/api/datasetTree" | |
18 | + fields='["result,provider_name","result,organization_name","result,package_name","result,resource_name","result,url","result,w"]' | |
19 | +></datasetexplorer-datalet> | |
20 | + | |
21 | + | |
22 | +</body> | |
23 | +</html> | |
0 | 24 | \ No newline at end of file | ... | ... |
datalets/datasetexplorer-datalet/docs.html
0 → 100644
1 | +<!DOCTYPE html> | |
2 | +<html lang="en"> | |
3 | +<head> | |
4 | + <link rel="import" href="../../bower_components/iron-component-page/iron-component-page.html"> | |
5 | + <meta charset="UTF-8"> | |
6 | +</head> | |
7 | +<body> | |
8 | + | |
9 | +<iron-component-page src="datasetexplorer-datalet.html"></iron-component-page> | |
10 | + | |
11 | +</body> | |
12 | +</html> | |
0 | 13 | \ No newline at end of file | ... | ... |
datalets/datasetexplorer-datalet/js/buildtreemap.js
0 → 100644
1 | +/** | |
2 | + * Created by Utente on 17/07/2015. | |
3 | + */ | |
4 | + | |
5 | +function build(root, place_holder) { | |
6 | + | |
7 | + var plwidth = $("#" + place_holder).width(), | |
8 | + plheight = $("#" + place_holder).height(); | |
9 | + | |
10 | + var margin = {top: 20, right: 0, bottom: 0, left: 0}, | |
11 | + //width = 960, | |
12 | + //height = 500 - margin.top - margin.bottom, | |
13 | + width = plwidth, | |
14 | + height = plheight - margin.top - margin.bottom, | |
15 | + formatNumber = d3.format(",d"), | |
16 | + transitioning; | |
17 | + | |
18 | + var x = d3.scale.linear() | |
19 | + .domain([0, width]) | |
20 | + .range([0, width]); | |
21 | + | |
22 | + var y = d3.scale.linear() | |
23 | + .domain([0, height]) | |
24 | + .range([0, height]); | |
25 | + | |
26 | + var treemap = d3.layout.treemap() | |
27 | + .children(function(d, depth) { return depth ? null : d._children; }) | |
28 | + .sort(function(a, b) { return a.value - b.value; }) | |
29 | + .ratio(height / width * 0.5 * (1 + Math.sqrt(5))) | |
30 | + .round(false); | |
31 | + | |
32 | + var svg = d3.select("#" + place_holder).append("svg") | |
33 | + .attr("width", width + margin.left + margin.right) | |
34 | + .attr("height", height + margin.bottom + margin.top) | |
35 | + .style("margin-left", -margin.left + "px") | |
36 | + .style("margin.right", -margin.right + "px") | |
37 | + .append("g") | |
38 | + .attr("transform", "translate(" + margin.left + "," + margin.top + ")") | |
39 | + .style("shape-rendering", "crispEdges"); | |
40 | + | |
41 | + var grandparent = svg.append("g") | |
42 | + .attr("class", "grandparent"); | |
43 | + | |
44 | + var dataletContainer = null; | |
45 | + | |
46 | + grandparent.append("rect") | |
47 | + .attr("y", -margin.top) | |
48 | + .attr("width", width) | |
49 | + .attr("height", margin.top); | |
50 | + | |
51 | + grandparent.append("text") | |
52 | + .attr("x", 6) | |
53 | + .attr("y", 6 - margin.top) | |
54 | + .attr("dy", ".75em"); | |
55 | + | |
56 | + //d3.json | |
57 | + initialize(root); | |
58 | + accumulate(root); | |
59 | + layout(root); | |
60 | + display(root); | |
61 | + | |
62 | + function initialize(root) { | |
63 | + root.x = root.y = 0; | |
64 | + root.dx = width; | |
65 | + root.dy = height; | |
66 | + root.depth = 0; | |
67 | + } | |
68 | + | |
69 | + // Aggregate the values for internal nodes. This is normally done by the | |
70 | + // treemap layout, but not here because of our custom implementation. | |
71 | + // We also take a snapshot of the original children (_children) to avoid | |
72 | + // the children being overwritten when when layout is computed. | |
73 | + function accumulate(d) { | |
74 | + return (d._children = d.children) | |
75 | + ? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 5) | |
76 | + : d.value; | |
77 | + } | |
78 | + | |
79 | + // Compute the treemap layout recursively such that each group of siblings | |
80 | + // uses the same size (1×1) rather than the dimensions of the parent cell. | |
81 | + // This optimizes the layout for the current zoom state. Note that a wrapper | |
82 | + // object is created for the parent node for each group of siblings so that | |
83 | + // the parent’s dimensions are not discarded as we recurse. Since each group | |
84 | + // of sibling was laid out in 1×1, we must rescale to fit using absolute | |
85 | + // coordinates. This lets us use a viewport to zoom. | |
86 | + function layout(d) { | |
87 | + if (d._children) { | |
88 | + treemap.nodes({_children: d._children}); | |
89 | + var i = 0; | |
90 | + d._children.forEach(function(c) { | |
91 | + c.x = d.x + c.x * d.dx; | |
92 | + c.y = d.y + c.y * d.dy; | |
93 | + c.dx *= d.dx; | |
94 | + c.dy *= d.dy; | |
95 | + c.parent = d; | |
96 | + c.depth = d.depth + 1; | |
97 | + c.color = c.depth < 2 | |
98 | + ? d3.scale.ordinal().domain(d3.range(d._children.length)).range(["#8dd3c7","#ffffb3","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5","#ffed6f"])(i++) | |
99 | + //? interpolate(0, d._children.length, i++) | |
100 | + : d.color; //d3.rgb(d.color).brighter(.5); | |
101 | + layout(c); | |
102 | + }); | |
103 | + } | |
104 | + } | |
105 | + | |
106 | + function display(d) {0 | |
107 | + grandparent | |
108 | + .datum(d.parent) | |
109 | + .on("click", transition) | |
110 | + .select("text") | |
111 | + .text(name(d)); | |
112 | + | |
113 | + var g1 = svg.insert("g", ".grandparent") | |
114 | + .datum(d) | |
115 | + .attr("class", "depth"); | |
116 | + | |
117 | + var g = g1.selectAll("g") | |
118 | + .data(d._children) | |
119 | + .enter().append("g"); | |
120 | + | |
121 | + g.filter(function(d) { return d._children; }) | |
122 | + .classed("children", true) | |
123 | + .on("click", transition); | |
124 | + | |
125 | + g.selectAll(".child") | |
126 | + .data(function(d) { return d._children || [d]; }) | |
127 | + .enter().append("rect") | |
128 | + .attr("class", "child") | |
129 | + .call(rect); | |
130 | + | |
131 | + g.append("rect") | |
132 | + .attr("class", "parent") | |
133 | + .call(rect) | |
134 | + .append("title") | |
135 | + .text(function(d) { return d.name; /*formatNumber(d.value);*/ }); | |
136 | + | |
137 | + g.append("text") | |
138 | + .attr("dy", ".75em") | |
139 | + .text(function(d) { return (d._children) ? d.name : ''; }) | |
140 | + //.style("font-size", function(d) { return Math.min(16, (d.dx - 8) / this.getComputedTextLength() * 16) + "px"; }) | |
141 | + .call(text) | |
142 | + ; | |
143 | + | |
144 | + function transition(d) { | |
145 | + if (transitioning || !d) return; | |
146 | + transitioning = true; | |
147 | + | |
148 | + if (dataletContainer) { | |
149 | + //svg.select("foreignObject") | |
150 | + //svg.remove(dataletContainer); | |
151 | + dataletContainer.remove(); | |
152 | + dataletContainer = null; | |
153 | + } | |
154 | + | |
155 | + var g2 = display(d), | |
156 | + t1 = g1.transition().duration(750), | |
157 | + t2 = g2.transition().duration(750); | |
158 | + | |
159 | + // Update the domain only after entering new elements. | |
160 | + x.domain([d.x, d.x + d.dx]); | |
161 | + y.domain([d.y, d.y + d.dy]); | |
162 | + | |
163 | + // Enable anti-aliasing during the transition. | |
164 | + svg.style("shape-rendering", null); | |
165 | + | |
166 | + // Draw child nodes on top of parent nodes. | |
167 | + svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; }); | |
168 | + | |
169 | + // Fade-in entering text. | |
170 | + g2.selectAll("text").style("fill-opacity", 0); | |
171 | + | |
172 | + // Transition to the new view. | |
173 | + t1.selectAll("text").call(text).style("fill-opacity", 0); | |
174 | + t2.selectAll("text").call(text).style("fill-opacity", 1); | |
175 | + t1.selectAll("rect").call(rect); | |
176 | + t2.selectAll("rect").call(rect); | |
177 | + | |
178 | + // Remove the old node when the transition is finished. | |
179 | + t1.remove().each("end", function() { | |
180 | + svg.style("shape-rendering", "crispEdges"); | |
181 | + transitioning = false; | |
182 | + }); | |
183 | + } | |
184 | + | |
185 | + if (!d._children[0]._children) { | |
186 | + var dataurl = d._children[0].name; | |
187 | + var pageurl = dataurl.replace(/\/download\/.*/, ''); | |
188 | + dataletContainer = svg | |
189 | + .append("foreignObject") | |
190 | + .attr("width", 480) | |
191 | + .attr("height", 500) | |
192 | + .append("xhtml:body") | |
193 | + .html('<iframe src="'+pageurl+'" width="'+root.dx+'" height="'+root.dy+'"></iframe>'); | |
194 | + | |
195 | + } | |
196 | + | |
197 | + return g; | |
198 | + } | |
199 | + | |
200 | + function text(text) { | |
201 | + text.attr("x", function(d) { return x(d.x) + 6; }) | |
202 | + .attr("y", function(d) { return y(d.y) + 6; }) | |
203 | + ; | |
204 | + } | |
205 | + | |
206 | + function rect(rect) { | |
207 | + rect.attr("x", function(d) { return x(d.x); }) | |
208 | + .attr("y", function(d) { return y(d.y); }) | |
209 | + .attr("width", function(d) { return x(d.x + d.dx) - x(d.x); }) | |
210 | + .attr("height", function(d) { return y(d.y + d.dy) - y(d.y); }) | |
211 | + .style("fill", function(d, i) { return d.color; }) | |
212 | + ; | |
213 | + } | |
214 | + | |
215 | + function name(d) { | |
216 | + return d.parent | |
217 | + ? name(d.parent) + "." + d.name | |
218 | + : d.name; | |
219 | + } | |
220 | +}; | |
0 | 221 | \ No newline at end of file | ... | ... |