概要
こちらのWebサービスで実際に触れます
【Where to Visit ?】
上記のWebサービスについては以下記事参照↓
GAE×DjangoでWebサービスを開発してみた
使った技術
- D3.js
- d3-cloudを利用して、WordCloudを作成した
- Ajax
- ページ遷移なしにWordCloudを変化させる
- Python3, Django
- バックエンド
説明
python, Django
DjangoをWebサービスのバックエンドとして利用している。
Ajaxも、基本的にはViewと同じで、word cloudのデータの受け渡しをする。
AjaxはJSONで送るので、datetime型はstrにして渡すなど、取扱に注意が必要。
# view用のメッソド
def photo(request):
# 省略
# ...
wordcloud_list = [
{
"word": "日本", # 表示される文字
"property": "country", # 種別
"count": 10, # prefecture=word
},
{
"word": "日本",
"property": "country",
"count": 10,
},
#...
]
output = {
"wordcloud_list": wordcloud_list,
}
return TemplateResponse(request, "ra/photo.html", output)
# ajax用のメソッド
def ajax(request):
if request.method == "GET":
# 省略
# datetimeはstrにして渡す
# ...
# response in JSON
res_data = {
"wordcloud_list": wordcloud_list,
}
response = json.dumps(res_data) # JSON形式に直して・・
return HttpResponse(response, content_type="text/javascript")
else:
raise Http404
HTML・javascript
view関数やajax用関数から受け取ったデータをもとに、D3.jsでWordCloudを描写する。
各WordはonclickをトリガーとしてWordCloudを描写する関数を再帰関数的に呼び出すようにしている。
<!-- 該当部分以外省略しています -->
<head>
<!-- Spinner -->
<link rel="stylesheet" href="{% static 'css/loading.css' %}">
</head>
<!--Spinner-->
<div id="overlay" class="hide">
<div class="cv-spinner">
<span class="spinner"></span>
</div>
</div>
<!--WordCloud-->
<div class="row">
<div class="col-12">
<div class="collapse show" id="collapseExample">
<div class="card">
<div id="wordcloud"></div>
</div>
</div>
</div>
</div>
<script src="https://d3js.org/d3.v5.min.js" charset="utf-8"></script>
<!-- 以下URL参照 -->
<!-- https://github.com/jasondavies/d3-cloud-->
<script src="{% static 'js/d3.layout.cloud.js' %}"></script>
<script>
var TARGET_ELEMENT_ID = '#wordcloud'; // 描画先
// Word Cloudを作成して、id="wordcloud"の要素に描写する関数
function draw_wc(data){
var random = d3.randomIrwinHall(2); // アーウィンホール分布
var countMax = d3.max(data, function(d){ return d.count} );
var sizeScale = d3.scaleLinear().domain([0, countMax]).range([10, 100])
var words = data.map(function(d) {
return {
text: d.word,
property: d.property,
size: sizeScale(d.count) //頻出カウントを文字サイズに反映
};
});
d3.layout.cloud().size([w, h])
.words(words)
.rotate(function() { return (~~(Math.random() * 6) - 3) * 30; })
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw) //描画関数の読み込み
.start();
return words;
// wordcloud 描画実行部分
function draw(words) {
d3.select(TARGET_ELEMENT_ID)
.append("svg")
.attr("class", "ui fluid image") // style using semantic ui
.attr("viewBox", "0 0 " + w + " " + h ) // ViewBox : x, y, width, height
.attr("width", "100%") // 表示サイズの設定
.attr("height", "100%") // 表示サイズの設定
.append("g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return d3.schemeCategory10[i % 10]; })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; })
// onclickをトリガーにでAjaxでWord Cloudを更新する
.on("click", function (d, i){
// Ajax
$.ajax({
url: "{% url 'ra:ajax' %}", //ajaxのurlはDjangoのurls.pyから取得
method: "GET",
data: {
text: d.text,
prop: d.property,
},
timeout: 10000,
dataType: "json",
//リクエストが完了するまで実行される
beforeSend: function(){
$('#overlay').removeClass('hide');
}
})
.done(function(data_ajax) {
// 写真・モーダルの更新
// 省略
// ...
// 既存Word Cloudを削除
d3.select(TARGET_ELEMENT_ID).select('svg').remove();
// 再帰関数的に呼び出して、再描写する
draw_wc(data_ajax.wordcloud_list);
// Spinner終了
$('#overlay').addClass('hide');
})
});
}
}
// 初回実行はDjangoから受け取ったデータを引数に、関数draw_wcを呼び出し
var data_list = [
{% for td in wordcloud_list %}
{
"word": "{{td.word}}",
"count": {{td.count}},
"property": "{{td.property}}",
},
{% endfor %}
]
words = draw_wc(data_list)
</script>
CSS
Ajax実行中はグルグル回るスピナーを表示させたかったので、CSSもを利用して実装した。
参考:https://pinkmonky.net/detail/?id=100
.hide {
display: none;
}
#overlay{
position: fixed;
top: 0;
margin: 0;
padding: 0;
z-index: 100;
width: 100%;
height:100%;
background: rgba(0,0,0,0.6);
}
.cv-spinner {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.spinner {
width: 40px;
height: 40px;
border: 4px #ddd solid;
border-top: 4px #2e93e6 solid;
border-radius: 50%;
animation: sp-anime 0.8s infinite linear;
}
@keyframes sp-anime {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}
.is-hide{
display:none;
}
困ったこと(解決できていない)
データ数が少ないと正しく表示されない
どうもデータ数が少ないと、一部データが欠落して、全件正しく表示されないようだ。
→暫定対策として、同じデータを重複させることでデータ数を無理やり増やした。。。見栄えは良くない。。。
ちなみに
今回のWebサービスでは、WordCloudだけではなく、写真などの入れ替えもAjaxで実装しています。
そのへんも今後記事にしていきたい。