Commit 38149ce95009363b120a01e064e1feeff27280aa

Authored by Luigi Serra
2 parents 35420b08 4f261647

Merge branch 'master' of http://service.routetopa.eu:7480/WebCompDev/COMPONENTS

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
... ...