d3-contour プラグインを用いての2Dカーネル密度推定のテスト。
Built with blockbuilder.org
license: mit | |
height: 960 |
d3-contour プラグインを用いての2Dカーネル密度推定のテスト。
Built with blockbuilder.org
// https://d3js.org/d3-contour/ Version 1.1.0. Copyright 2017 Mike Bostock. | |
!function(t,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("d3-array")):"function"==typeof define&&define.amd?define(["exports","d3-array"],r):r(t.d3=t.d3||{},t.d3)}(this,function(t,r){"use strict";function n(t,r){for(var n=r[0],i=r[1],a=-1,o=0,h=t.length,f=h-1;o<h;f=o++){var u=t[o],c=u[0],d=u[1],l=t[f],s=l[0],g=l[1];if(e(u,l,r))return 0;d>i!=g>i&&n<(s-c)*(i-d)/(g-d)+c&&(a=-a)}return a}function e(t,r,n){var e;return i(t,r,n)&&a(t[e=+(t[0]===r[0])],n[e],r[e])}function i(t,r,n){return(r[0]-t[0])*(n[1]-t[1])==(n[0]-t[0])*(r[1]-t[1])}function a(t,r,n){return t<=r&&r<=n||n<=r&&r<=t}function o(t,r,n){for(var e=t.width,i=t.height,a=1+(n<<1),o=0;o<i;++o)for(var h=0,f=0;h<e+n;++h)h<e&&(f+=t.data[h+o*e]),h>=n&&(h>=a&&(f-=t.data[h-a+o*e]),r.data[h-n+o*e]=f/Math.min(h+1,e-1+a-h,a))}function h(t,r,n){for(var e=t.width,i=t.height,a=1+(n<<1),o=0;o<e;++o)for(var h=0,f=0;h<i+n;++h)h<i&&(f+=t.data[o+h*e]),h>=n&&(h>=a&&(f-=t.data[o+(h-a)*e]),r.data[o+(h-n)*e]=f/Math.min(h+1,i-1+a-h,a))}function f(t){return t[0]}function u(t){return t[1]}var c=Array.prototype,d=c.slice,l=function(t,r){return t-r},s=function(t){for(var r=0,n=t.length,e=t[n-1][1]*t[0][0]-t[n-1][0]*t[0][1];++r<n;)e+=t[r-1][1]*t[r][0]-t[r-1][0]*t[r][1];return e},g=function(t){return function(){return t}},v=function(t,r){for(var e,i=-1,a=r.length;++i<a;)if(e=n(t,r[i]))return e;return 0},w=function(){},p=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,.5],[.5,1]],[[1,1.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,1.5]],[[1.5,1],[1,.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]],y=function(){function t(t){var e=h(t);if(Array.isArray(e))e=e.slice().sort(l);else{var i=r.extent(t),a=i[0],o=i[1];e=r.tickStep(a,o,e),e=r.range(Math.floor(a/e)*e,Math.floor(o/e)*e,e)}return e.map(function(r){var e=[],i=[];return n(t,r,function(n){f(n,t,r),s(n)>0?e.push([n]):i.push(n)}),i.forEach(function(t){for(var r,n=0,i=e.length;n<i;++n)if(v((r=e[n])[0],t))return void r.push(t)}),e}).map(function(t,r){return{type:"MultiPolygon",value:e[r],coordinates:t}})}function n(t,r,n){function i(t){var r,i,a=[t[0][0]+h,t[0][1]+f],o=[t[1][0]+h,t[1][1]+f],u=e(a),c=e(o);(r=g[u])?(i=s[c])?(delete g[r.end],delete s[i.start],r===i?(r.ring.push(o),n(r.ring)):s[r.start]=g[i.end]={start:r.start,end:i.end,ring:r.ring.concat(i.ring)}):(delete g[r.end],r.ring.push(o),g[r.end=c]=r):(r=s[c])?(i=g[u])?(delete s[r.start],delete g[i.end],r===i?(r.ring.push(o),n(r.ring)):s[i.start]=g[r.end]={start:i.start,end:r.end,ring:i.ring.concat(r.ring)}):(delete s[r.start],r.ring.unshift(a),s[r.start=u]=r):s[u]=g[c]={start:u,end:c,ring:[a,o]}}var h,f,u,c,d,l,s=new Array,g=new Array;for(h=f=-1,c=t[0]>=r,p[c<<1].forEach(i);++h<a-1;)u=c,c=t[h+1]>=r,p[u|c<<1].forEach(i);for(p[c<<0].forEach(i);++f<o-1;){for(h=-1,c=t[f*a+a]>=r,d=t[f*a]>=r,p[c<<1|d<<2].forEach(i);++h<a-1;)u=c,c=t[f*a+a+h+1]>=r,l=d,d=t[f*a+h+1]>=r,p[u|c<<1|d<<2|l<<3].forEach(i);p[c|d<<3].forEach(i)}for(h=-1,d=t[f*a]>=r,p[d<<2].forEach(i);++h<a-1;)l=d,d=t[f*a+h+1]>=r,p[d<<2|l<<3].forEach(i);p[d<<3].forEach(i)}function e(t){return 2*t[0]+t[1]*(a+1)*4}function i(t,r,n){t.forEach(function(t){var e,i=t[0],h=t[1],f=0|i,u=0|h,c=r[u*a+f];i>0&&i<a&&f===i&&(e=r[u*a+f-1],t[0]=i+(n-e)/(c-e)-.5),h>0&&h<o&&u===h&&(e=r[(u-1)*a+f],t[1]=h+(n-e)/(c-e)-.5)})}var a=1,o=1,h=r.thresholdSturges,f=i;return t.size=function(r){if(!arguments.length)return[a,o];var n=Math.ceil(r[0]),e=Math.ceil(r[1]);if(!(n>0&&e>0))throw new Error("invalid size");return a=n,o=e,t},t.thresholds=function(r){return arguments.length?(h="function"==typeof r?r:g(Array.isArray(r)?d.call(r):r),t):h},t.smooth=function(r){return arguments.length?(f=r?i:w,t):f===i},t},M=function(){function t(t){var e=new Float32Array(A*m),i=new Float32Array(A*m);t.forEach(function(t,r,n){var i=l(t,r,n)+E>>M,a=s(t,r,n)+E>>M;i>=0&&i<A&&a>=0&&a<m&&++e[i+a*A]}),o({width:A,height:m,data:e},{width:A,height:m,data:i},p>>M),h({width:A,height:m,data:i},{width:A,height:m,data:e},p>>M),o({width:A,height:m,data:e},{width:A,height:m,data:i},p>>M),h({width:A,height:m,data:i},{width:A,height:m,data:e},p>>M),o({width:A,height:m,data:e},{width:A,height:m,data:i},p>>M),h({width:A,height:m,data:i},{width:A,height:m,data:e},p>>M);var a=z(e);if(!Array.isArray(a)){var f=r.max(e);a=r.tickStep(0,f,a),a=r.range(0,Math.floor(f/a)*a,a),a.shift()}return y().thresholds(a).size([A,m])(e).map(n)}function n(t){return t.value*=Math.pow(2,-2*M),t.coordinates.forEach(e),t}function e(t){t.forEach(i)}function i(t){t.forEach(a)}function a(t){t[0]=t[0]*Math.pow(2,M)-E,t[1]=t[1]*Math.pow(2,M)-E}function c(){return E=3*p,A=v+2*E>>M,m=w+2*E>>M,t}var l=f,s=u,v=960,w=500,p=20,M=2,E=3*p,A=v+2*E>>M,m=w+2*E>>M,z=g(20);return t.x=function(r){return arguments.length?(l="function"==typeof r?r:g(+r),t):l},t.y=function(r){return arguments.length?(s="function"==typeof r?r:g(+r),t):s},t.size=function(t){if(!arguments.length)return[v,w];var r=Math.ceil(t[0]),n=Math.ceil(t[1]);if(!(r>=0||r>=0))throw new Error("invalid size");return v=r,w=n,c()},t.cellSize=function(t){if(!arguments.length)return 1<<M;if(!((t=+t)>=1))throw new Error("invalid cell size");return M=Math.floor(Math.log(t)/Math.LN2),c()},t.thresholds=function(r){return arguments.length?(z="function"==typeof r?r:g(Array.isArray(r)?d.call(r):r),t):z},t.bandwidth=function(t){if(!arguments.length)return Math.sqrt(p*(p+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return p=Math.round((Math.sqrt(4*t*t+1)-1)/2),c()},t};t.contours=y,t.contourDensity=M,Object.defineProperty(t,"__esModule",{value:!0})}); |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"/> | |
<title></title> | |
<style> | |
html, body { | |
width: 100%; | |
height: 100%; | |
padding: 0px; | |
margin: 0px; | |
} | |
#map { | |
width: 960px; | |
height: 960px; | |
} | |
.land:hover { | |
fill:red; | |
} | |
</style> | |
</head> | |
<body> | |
<svg id="map"></svg> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script> | |
<script src="d3-contour.v1.min.js"></script> | |
<script src="kodama.js"></script> | |
<script> | |
!(function(){ | |
"use strict"; | |
var w = 1500; | |
var h = 1500; | |
var format = d3.format(".4f"); | |
var svg = d3.select("#map") | |
.attr("viewBox", "0 0 1500 1500") | |
; | |
var g = svg.append("g") | |
var zoomed = function() { | |
g.attr("transform", d3.event.transform); | |
} | |
svg.call(d3.zoom() | |
.scaleExtent([1 / 2, 12]) | |
.on("zoom", zoomed)); | |
var aedLayer = g.append("g"); | |
var landLayer = g.append("g"); | |
var tooltipFormat = function(d, i){ | |
return { | |
title:d.properties.MOJI | |
}; | |
}; | |
d3.queue() | |
.defer(d3.json, 'takasaki.geojson') | |
.defer(d3.json, 'aed.geojson') | |
.await(function(error, land, aed){ | |
var projection = d3.geoMercator() | |
.fitExtent([[0,0], [w, h]], land); | |
aed.features.forEach(function(d){ | |
d.geometry.coordinates[0] = +format(d.geometry.coordinates[0]); | |
d.geometry.coordinates[1] = +format(d.geometry.coordinates[1]); | |
var xy = projection(d.geometry.coordinates); | |
d._x = +format(xy[0]); | |
d._y = +format(xy[1]); | |
}); | |
var geoPath = d3.geoPath().projection(projection); | |
var land = landLayer.selectAll(".land") | |
.data(land.features) | |
.enter() | |
.append("path") | |
.attr("d", geoPath) | |
.attr("class", "land") | |
.attr("fill-opacity", 0.2) | |
.attr("stroke", "black") | |
.call(d3.kodama.tooltip().format(tooltipFormat)) | |
; | |
var contours = d3.contourDensity() | |
.thresholds(20) | |
.cellSize(1) | |
.bandwidth(10) | |
.size([w,h]) | |
.x(function(d){ return d._x ;}) | |
.y(function(d){ return d._y ;}) | |
; | |
var aedContours = aedLayer.append("g") | |
.selectAll(".contours") | |
.data(contours(aed.features)) | |
.enter() | |
.append("path") | |
.attr("d", d3.geoPath()) | |
.attr("fill", function(d){ return "blue" ;}) | |
.attr("fill-opacity", 0.2) | |
.attr("stroke", "none") | |
; | |
var point = aedLayer.append("g") | |
.selectAll(".point") | |
.data(aed.features) | |
.enter() | |
.append("circle") | |
.attr("cx", function(d){ return d._x ;}) | |
.attr("cy", function(d){ return d._y ;}) | |
.attr("r", 1) | |
; | |
}); | |
}()); | |
</script> | |
</body> | |
</html> |
/** | |
* kodama.js (v2.0.0) | |
* | |
* Copyright (c) 2015 Scott Southworth & Contributors | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this | |
* file except in compliance with the License. You may obtain a copy of the License at: | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software distributed under | |
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF | |
* ANY KIND, either express or implied. See the License for the specific language | |
* governing permissions and limitations under the License. | |
* | |
* @authors Scott Southworth @DarkMarmot scott.southworth@gmail.com | |
* | |
*/ | |
;(function (root, factory) { | |
if (typeof define === 'function' && define.amd) { | |
define(['d3'], factory); | |
} else if (typeof module === 'object' && module.exports) { | |
module.exports = function(d3) { | |
d3.kodama = factory(d3); | |
return d3.kodama; | |
} | |
} else { | |
root.d3.kodama = root.d3.bamboo = factory(root.d3); | |
} | |
}(this, function (d3) { | |
if(d3.kodama) return d3.kodama; | |
var kodama = {}; | |
var validOptions = ['gravity','theme','distance','style','target','by', | |
'fadeInDuration', 'fadeOutDuration', 'holdDuration']; | |
var initialized = false; | |
var bodyNode = null; //d3.select('body').node(); | |
var baseSel = null; | |
var tipSel = null; | |
var holderSel = null; | |
var fadingState = "none"; | |
var gravityDefs = { | |
northwest: [-1, -1], | |
topleft: [-1, -1], | |
upperleft: [-1, -1], | |
northeast: [1, -1], | |
topright: [1, -1], | |
upperright: [1, -1], | |
southwest: [-1, 1], | |
bottomleft: [-1, 1], | |
lowerleft: [-1, 1], | |
southeast: [1, 1], | |
bottomright: [1, 1], | |
lowerright: [1, 1], | |
north: [0, -1], | |
top: [0, -1], | |
south: [0, 1], | |
bottom: [0, -1], | |
west: [-1, 0], | |
left: [-1, 0], | |
right: [1, 0], | |
east: [1, 0], | |
center: [0, 0] | |
}; | |
function resolveGravity(name) { | |
name = name.split('-').join(); | |
return gravityDefs[name]; | |
} | |
var themesByName = {}; | |
var offsetSwitch = [0, 0]; | |
var offsetKey = "0:0"; | |
var tipDisplayData = null; | |
var activated = false; // to display after delay | |
var lastSourceDataShown; | |
var lastFormatFuncShown; | |
var defaultTarget = null; | |
var defaultHoldDuration = 0; | |
var defaultFadeInDuration = 0; | |
var defaultFadeOutDuration = 500; | |
var defaultThemeName = 'kodama'; | |
var defaultGravityDirection = 'top'; | |
var defaultByDirection = 'top'; | |
//resolveGravity removes '-' from user input, ex: 'lower-right' --> 'lowerright' | |
var defaultGravity = resolveGravity(defaultGravityDirection); | |
var defaultBy = resolveGravity(defaultByDirection); | |
var defaultDistance = 25; | |
var defaultTheme = themesByName[defaultThemeName] = { | |
frame: { | |
padding: '4px', | |
background: 'linear-gradient(to top, rgb(16, 74, 105) 0%, rgb(14, 96, 125) 90%)', | |
'font-family': '"Helvetica Neue", Helvetica, Arial, sans-serif', | |
'border': '1px solid rgb(57, 208, 204)', | |
color: 'rgb(245,240,220)', | |
'border-radius': '4px', | |
'font-size': '12px', | |
'box-shadow': '0px 1px 3px rgba(0,20,40,.5)' | |
}, | |
title: {'text-align': 'center', 'padding': '4px'}, | |
item_title: {'text-align': 'right', 'color': 'rgb(220,200,120)'}, | |
item_value: {'padding': '1px 2px 1px 10px', 'color': 'rgb(234, 224, 184)'} | |
}; | |
//lets you pass an object with multiple styles to d3 using .call() | |
var multiStyles = function (styles) { | |
return function(selection) { | |
for (var property in styles) { | |
selection.style(property, styles[property]); | |
} | |
}; | |
} | |
kodama.init = function(node){ | |
bodyNode = node || document.body; | |
if(baseSel) | |
baseSel.remove(); | |
// handles activity, delay into being transitions | |
baseSel = d3.select(bodyNode) | |
.append('div') | |
.style('position', 'absolute') | |
.style('left', 0) | |
.style('top', 0) | |
.style('visibility', 'hidden') | |
.style('pointer-events', 'none') | |
.attr('class','kodama-tooltip') | |
.attr('name', 'kodama'); | |
// handles mouse position placement and fade out transitions | |
tipSel = baseSel | |
.append('div') | |
.attr('name', 'kodamaTip') | |
.call(multiStyles({'position': 'relative', 'pointer-events': 'none', 'z-index': 9999})) | |
.style('opacity', 0); | |
// handles screen gravity offset placement and transitions | |
holderSel = tipSel.append('div').style('position', 'relative'); | |
}; | |
if(document.readyState === 'loading'){ | |
document.addEventListener('DOMContentLoaded', function(){ kodama.init(); }); | |
} else { | |
kodama.init(); | |
} | |
kodama.holdDuration = function(duration){ | |
if(arguments.length === 0) return defaultHoldDuration; | |
defaultHoldDuration = duration; | |
return kodama; | |
}; | |
kodama.fadeInDuration = function(duration){ | |
if(arguments.length === 0) return defaultFadeInDuration; | |
defaultFadeInDuration = duration; | |
return kodama; | |
}; | |
kodama.fadeOutDuration = function(duration){ | |
if(arguments.length === 0) return defaultFadeOutDuration; | |
defaultFadeOutDuration = duration; | |
return kodama; | |
}; | |
kodama.distance = function(distance){ | |
if(arguments.length === 0) return defaultDistance; | |
defaultDistance = distance || 25; | |
return kodama; | |
}; | |
kodama.gravity = function(direction){ | |
if(arguments.length === 0) return defaultGravityDirection; | |
defaultGravityDirection = direction || 'top'; | |
defaultGravity = resolveGravity(defaultGravityDirection); | |
return kodama; | |
}; | |
kodama.theme = function(name){ | |
if(arguments.length === 0) return defaultThemeName; | |
defaultThemeName = name; | |
defaultTheme = themesByName[defaultThemeName]; | |
return kodama; | |
}; | |
kodama.themeRegistry = function(name, config){ | |
if(arguments.length === 1) return themesByName[name]; | |
themesByName[name] = config; | |
return kodama; | |
}; | |
kodama.themeRegistry('white_wolf', { | |
frame: { | |
padding: '5px', | |
background: 'linear-gradient(to top, rgba(220, 230, 240, .6) 0%, rgba(235, 240, 245, .9) 90%, rgba(230, 235, 240, .8) 100%)', | |
'font-family': '"Helvetica Neue", Helvetica, Arial, sans-serif', | |
'border': '1px solid rgb(220, 230, 250)', | |
'border-radius': '6px', | |
'font-size': '14px', | |
'box-shadow': '0px 1px 3px rgba(0,20,70,.5)' | |
}, | |
title: {'text-align': 'center', 'padding': '4px', color: 'rgb(115,130,140)', 'font-size': '15px','text-shadow': '0 -1px 0 rgba(255,255,255,.5)'}, | |
item_title: {'text-align': 'right', 'color': 'rgb(80,100,110)','font-size': '14px','text-shadow': '0 -1px 0 rgba(255,255,255,.5)'}, | |
item_value: {'padding': '1px 2px 1px 10px', 'color': 'rgb(90, 95, 85)','font-size': '14px','text-shadow': '0 -1px 0 rgba(255,255,255,.5)'} | |
}); | |
// returns a function/object with a config api and accepting a d3 selection to wire handlers | |
kodama.tooltip = function() { | |
var _offsets = {}; | |
var _options = undefined; | |
var _sourceKey = undefined; | |
var _sourceData = undefined; | |
var _formatFunc = null; | |
var _distance = defaultDistance; | |
//user input gravity direction | |
var _gravityDirection = defaultGravityDirection; | |
//resolved gravity direction | |
var _gravity = defaultGravity; | |
var _byDirection = defaultByDirection; | |
var _by = defaultBy; | |
var _theme = defaultTheme; | |
var _holdDuration = defaultHoldDuration; | |
var _fadeInDuration = defaultFadeInDuration; | |
var _fadeOutDuration = defaultFadeOutDuration; | |
var _target = defaultTarget; | |
var attrs = {}; | |
var styles = {}; | |
var _tooltip = function _tooltip(selection) { | |
selection | |
.on('mouseover.tooltip', function (d, i) { | |
_tooltip.show(d, i); | |
}) | |
.on('mousedown.tooltip', function () { | |
_tooltip.show(null) | |
}) | |
.on('mouseup.tooltip', function () { | |
_tooltip.show(null) | |
}) | |
.on('mousemove.tooltip', function () { | |
_tooltip._update(); | |
}) | |
.on('mouseout.tooltip', function () { | |
_tooltip.show(null) | |
}); | |
}; | |
_tooltip._build = function _build() { | |
//at this point _tooltip.show has been activated... so the tooltip is redrawn on any mouse event on an element | |
if (!tipDisplayData) { | |
_tooltip.fadeOut(); | |
return; | |
} else { | |
_tooltip.activateAfter(_holdDuration); | |
} | |
holderSel.selectAll('*').remove(); | |
holderSel | |
.append('div') | |
// .attr(attrs) | |
.call(multiStyles(_theme.frame)) | |
.datum(tipDisplayData) | |
.each(function (d) { | |
var sel = d3.select(this); | |
if (d.title) { | |
sel | |
.append('div') | |
.call(multiStyles(_theme.title)) | |
.append('span') | |
.html(d.title); | |
} | |
if (d.items) { | |
var tbody = sel.append('table').append('tbody'); | |
tbody.selectAll('tr').data(d.items) | |
.enter() | |
.append('tr') | |
.each(function (item) { | |
var tr = d3.select(this); | |
var titleCell = tr.append('td'); | |
var valueCell = tr.append('td'); | |
titleCell.html(item.title + ':').style(_theme.item_title); | |
valueCell.html(item.value).style(_theme.item_value); | |
}); | |
} | |
}); | |
var xOff = holderSel.node().clientWidth / 2; | |
var yOff = holderSel.node().clientHeight / 2; | |
/*if there's a new offset, reposition tooltip by resetting _offsets... | |
when _tooltip._update(true) is called it sets justRebuilt to true, which | |
activates repositioning */ | |
for (var i = -1; i <= 1; i++) { | |
for (var j = -1; j <= 1; j++) { | |
var k = i + ":" + j; | |
_offsets[k] = {left: i * (xOff + _distance) + 'px', top: j * (yOff + _distance) + 'px'}; | |
} | |
} | |
_tooltip._update(true); | |
}; | |
_tooltip._update = function _update(justRebuilt) { | |
if (!tipDisplayData) return; | |
function getTargetPos(target, by){ | |
if(!target) return d3.mouse(bodyNode); | |
var bounds = target.getBoundingClientRect(); | |
var xOff = bounds.width / 2; | |
var yOff = bounds.height / 2; | |
return [bounds.left + (by[0] + 1) * xOff, bounds.top + (by[1] + 1) * yOff]; | |
} | |
//var bounds = _target ? _target.getBoundingClientRect() : null; | |
var pos = getTargetPos(_target, _by); //bounds ? [bounds.left, bounds.top] : d3.mouse(bodyNode); | |
var x = pos[0]; | |
var y = pos[1]; | |
var bw = bodyNode.clientWidth; | |
var bh = bodyNode.clientHeight; | |
var tw = tipSel.node().clientWidth; | |
var th = tipSel.node().clientHeight; | |
var bestKey = null; | |
var bestDiff = 5; | |
var xkMax = (x > bw - tw) ? -1 : (x > bw - tw - _distance * 2 ? 0 : 1); | |
var ykMax = (y > bh - th) ? -1 : (y > bh - th - _distance * 2 ? 0 : 1); | |
var xkMin = (x < tw) ? 1 : (x < tw + _distance * 2 ? 0 : -1); | |
var ykMin = (y < th) ? 1 : (y < th + _distance * 2 ? 0 : -1); | |
for(var xk = xkMin; xk <= xkMax; xk++){ | |
for(var yk = ykMin; yk <= ykMax; yk++){ | |
if(xk === 0 && yk === 0 && _gravity[0] !== 0 && _gravity[1] !== 0) continue; // skip center unless specified | |
var diff = Math.abs(xk - _gravity[0]) + Math.abs(yk - _gravity[1]); | |
if(diff < bestDiff) { | |
bestKey = [xk, yk]; | |
bestDiff = diff; | |
} | |
} | |
} | |
bestKey = bestKey || [0, 0]; | |
var left = x - tw / 2; | |
var top = y - th / 2; | |
tipSel.call(multiStyles({ | |
left: left + 'px', | |
top: top + 'px' | |
})) | |
var k = bestKey[0] + ':' + bestKey[1]; | |
var moved = Math.max(Math.abs(offsetSwitch[0] - x), Math.abs(offsetSwitch[1] - y)); | |
//delayed reposition of tooltip if new offset | |
if (justRebuilt || (k !== offsetKey && moved > _distance)) { | |
offsetKey = k; | |
offsetSwitch = pos; | |
var offsetStyle = _offsets[k]; | |
holderSel | |
.transition().ease(d3.easeCubicOut).duration(250) | |
.call(multiStyles(offsetStyle)); | |
} | |
}; | |
// deprecated | |
_tooltip.attr = function (_x) { | |
if (!arguments.length) return attrs; | |
attrs = _x; | |
return this; | |
}; | |
// deprecated | |
_tooltip.style = _tooltip.css = function (_x) { | |
if (!arguments.length) return styles; | |
styles = _x; | |
return this; | |
}; | |
_tooltip.fadeIn = function(){ | |
if(fadingState === 'in') { | |
return; | |
} | |
fadingState = 'in'; | |
var progress = tipSel.style('opacity') / 1.0; | |
var duration = (1.0 - progress) * _fadeInDuration; | |
tipSel.interrupt().transition().duration(duration).style('opacity', 1); | |
}; | |
_tooltip.fadeOut = function(){ | |
if(fadingState === 'out') { | |
return; | |
} | |
fadingState = 'out'; | |
var progress = tipSel.style('opacity') / 1.0; | |
var duration = progress * _fadeOutDuration; | |
tipSel.transition().delay(50).duration(duration).style('opacity', 0); | |
}; | |
_tooltip.activate = function(){ | |
activated = true; | |
_tooltip.fadeIn(); | |
}; | |
_tooltip.deactivate = function(){ | |
lastSourceDataShown = null; | |
activated = false; | |
_tooltip.fadeOut(); | |
}; | |
_tooltip.activateAfter = function(){ | |
baseSel.interrupt().transition(); | |
if(!activated) { | |
baseSel.transition() | |
.delay(_holdDuration) | |
.duration(0) | |
.on('start', _tooltip.activate) | |
.style('visibility','visible'); | |
} else { | |
_tooltip.fadeIn(); | |
} | |
}; | |
_tooltip.theme = function(name){ | |
if(arguments.length === 0) return _theme; | |
_theme = themesByName[name]; | |
return this; | |
}; | |
_tooltip.target = function(target){ | |
if(arguments.length === 0) return _target; | |
var node = target; | |
while(node && node.length > 0){ | |
node = node[0]; | |
} | |
_target = node; | |
return this; | |
}; | |
_tooltip.holdDuration = function(duration){ | |
if(arguments.length === 0) return _holdDuration; | |
_holdDuration = duration; | |
return this; | |
}; | |
_tooltip.fadeInDuration = function(duration){ | |
if(arguments.length === 0) return _fadeInDuration; | |
_fadeInDuration = duration; | |
return this; | |
}; | |
_tooltip.fadeOutDuration = function(duration){ | |
if(arguments.length === 0) return _fadeOutDuration; | |
_fadeOutDuration = duration; | |
return this; | |
}; | |
_tooltip.distance = function (distance) { | |
if(arguments.length === 0) return _distance; | |
_distance = distance; | |
return this; | |
}; | |
_tooltip.gravity = function (direction) { | |
if(arguments.length === 0) return _gravity; | |
_gravityDirection = direction; | |
_gravity = resolveGravity(_gravityDirection); | |
return this; | |
}; | |
_tooltip.by = function (direction) { | |
if(arguments.length === 0) return _by; | |
_byDirection = direction; | |
_by = resolveGravity(_byDirection); | |
return this; | |
}; | |
_tooltip.options = function(options) { | |
if(arguments.length === 0) return _options; | |
if(!options) return this; | |
if(options.theme){ // if a theme is specified | |
_tooltip.theme(options.theme); | |
if(_theme.options){ // apply any options in the theme as defaults if not specified in the options argument | |
for(var prop2 in _theme.options){ | |
if(!options.hasOwnProperty(prop2)) | |
options[prop2] = _theme.options[prop2]; | |
} | |
} | |
} | |
for(var prop in options){ | |
if(validOptions.indexOf(prop)===-1) continue; | |
_tooltip[prop](options[prop]); | |
} | |
_options = options; | |
return this; | |
}; | |
_tooltip.format = _tooltip.prep = _tooltip.data = function(formatFunc) { | |
_formatFunc = formatFunc; | |
return this; | |
}; | |
//_tooltip.show is called for all mouse events on each selection (in & out) | |
_tooltip.show = function(sourceData, sourceKey){ | |
_sourceData = sourceData; | |
_sourceKey = sourceKey; | |
//if new sourceData or new format | |
if(_sourceData !== lastSourceDataShown || _formatFunc !== lastFormatFuncShown){ | |
lastFormatFuncShown = _formatFunc; | |
lastSourceDataShown = _sourceData; | |
tipDisplayData = (_formatFunc && _sourceData) ? _formatFunc(_sourceData, _sourceKey) : _sourceData; | |
_tooltip.options(tipDisplayData); | |
_tooltip._build(); | |
} | |
_tooltip._update(); | |
}; | |
return _tooltip; | |
}; | |
var selector = typeof jQuery !== 'undefined' && jQuery !== null ? jQuery : null; | |
selector = selector || (typeof Zepto !== 'undefined' && Zepto !== null ? Zepto : null); | |
if(selector) { | |
selector.fn.kodama = $.fn.kodama_tooltip = $.fn.bamboo = $.fn.kodama || function(tooltipData){ | |
var self = this; | |
var els = self.toArray(); | |
var arr = d3.range(els.length).map(function(){return tooltipData;}); | |
d3.selectAll(els).data(arr).call(d3.kodama.tooltip()); | |
return this; | |
}; | |
} | |
return kodama; | |
})); |