amChartsは,地図やグラフを簡単に作成できるライブラリです. 今回は,地図上にデータを表示するサンプルをご紹介します.
サンプル
カスタマイズビューにて,日本地図上にコロナ感染者のデータを表示します. 円の大きさでデータを可視化します.
※東京の円は本州上に表示されませんが,仕様です.(恐らく大島なども含めて中心を決めているのかと)
サンプルでは下記サイトのデータを引用しました.
https://www.stopcovid19.jp/
フォーム設定
レコード追加
Node.jsを用いてレコードの追加が可能です. 必要パッケージを取得後,下記「getCOVIT19.js」を実行します.
・getCOVIT19.js
const{KintoneRestAPIClient}=require("@kintone/rest-api-client");constrequest=require('request-promise');constcsvParse=require('csv-parse');constmoment=require('moment');require('twix');constclient=newKintoneRestAPIClient({baseUrl:'https://\*\*\*\*.cybozu.com',//kintoneのURLauth:{username:'\*\*\*\*',//kintoneのログインユーザ名password:'\*\*\*\*',//kintoneのログインパスワード},});constapp= **** ;//レコード追加先アプリconststartDate='2020-03-18';constendDate='2020-05-21';constcsvUrl=date=\>`https://www.stopcovid19.jp/data/covid19japan/${date}.csv`;constdates=moment.twix(startDate,endDate).toArray('days').map(day=\>day.format('YYYY-MM-DD'));Promise.all(dates.map(date=\>request(csvUrl(date)))).then(bodys=\>Promise.all(bodys.map((body,i)=\>newPromise(resolve=\>csvParse(body.replace(/"/g,''),{bom:true,quote: null,columns:true,},(e,rows)=\>resolve(rows)))))).then(data=\>{constrecords=data.map((rows,i)=\>({date:{value:dates[i]},table:{value:rows.map(row=\>({value:Object.fromEntries(Object.entries(row).filter(([key,value])=\>(key&&value)).map(([key,value])=\>[key,{value:value}]))}))}}));client.record.addAllRecords({app,records});});
・コマンド
$ npm i @kintone/rest-api-client request request-promise csv-parse moment twix
$ node getCOVIT19.js
コード
HTML(カスタマイズビュー)
一覧名は「amCharts」とします.
\<divid="chart-controllers"\>\</div\>\<divid="chartdiv"style="width: 100%; height: 60vh;"\>\</div\>
CSS
下記を読み込みます.
- kintone-ui-component.min.css (こちらからダウンロード.)
JavaScript
下記を順に読み込みます.
- https://www.amcharts.com/lib/4/core.js
- https://www.amcharts.com/lib/4/maps.js
- https://www.amcharts.com/lib/4/geodata/japanLow.js
- kintone-ui-component.min.js (こちらからダウンロード.)
- sample.js (下記)
・sample.js
(function(){"use strict";kintone.events.on(['app.record.index.show',],function(event){//設定値varviewName='amCharts';varchartDomId='chartdiv';varchartControllersDomId='chart-controllers';vardateCode='date';vartableCode='table';varprefectureNameCode='name';varprefectureJpNameCode='name\_jp';varcolumnCodes=['npatients','ncurrentpatients','nexits','ndeaths','nheavycurrentpatients','nunknowns','ninspections',];varcolumnColors=['#f00','#0f0','#00f','#ff0','#f0f','#0ff','#000',];varmaxCircleRadius=30;if(event.viewName!==viewName||!event.records.length)return;//データ取得用関数定義varprefectureMapper=am4geodata\_japanLow.features.reduce(function(prefectureMapper,feature){prefectureMapper[feature.properties.name]=feature.properties.id;returnprefectureMapper;},{});vargetData=function(dateIndex,tableColumnIndex){returnevent.records[dateIndex][tableCode].value.map(function(row){return{id:prefectureMapper[row.value[prefectureNameCode].value],name:row.value[prefectureJpNameCode].value,value:row.value[columnCodes[tableColumnIndex]].value};});};vargetMaxValue=function(tableColumnIndex){//選択したカラムについて,全レコードから最大値を取得returnevent.records.reduce(function(max,record){returnrecord[tableCode].value.reduce(function(max,row){returnMath.max(max,row.value[columnCodes[tableColumnIndex]].value);},max);},0);};//図表作成(https://www.amcharts.com/docs/v4/reference/mapchart/)varchart=am4core.create(chartDomId,am4maps.MapChart);chart.geodata=am4geodata\_japanLow;chart.projection=newam4maps.projections.Miller();//MapPolygonSeries追加(https://www.amcharts.com/docs/v4/reference/mappolygonseries/)varpolygonSeries=chart.series.push(newam4maps.MapPolygonSeries());polygonSeries.useGeodata=true;varpolygonTemplate=polygonSeries.mapPolygons.template;polygonTemplate.tooltipText='{name}: {value}';polygonTemplate.tooltipPosition='fixed';//MapImageSeries追加(https://www.amcharts.com/docs/v4/reference/mapimageseries/)varbubbleSeries=chart.series.push(newam4maps.MapImageSeries());bubbleSeries.dataFields.id='id';bubbleSeries.dataFields.value='value';varimageTemplate=bubbleSeries.mapImages.template;imageTemplate.tooltipText='{name}: {value}';//円作成varcircle=imageTemplate.createChild(am4core.Circle);circle.fillOpacity=0.5;varcircleHeatRule={target:circle,property:'radius',min:1,max:maxCircleRadius,minValue:0,dataField:'value',};bubbleSeries.heatRules.push(circleHeatRule);//valueが0の円を非表示bubbleSeries.events.on("dataitemsvalidated",function(){bubbleSeries.dataItems.each(function(dataItem){varmapImage=dataItem.mapImage;varcircle=mapImage.children.getIndex(0);if(mapImage.dataItem.value==0){circle.hide(0);}elseif(circle.isHidden||circle.isHiding){circle.show();}});});//円の位置調整['latitude','longitude'].forEach(function(adapter){imageTemplate.adapter.add(adapter,function(value,target){varpolygon=polygonSeries.getPolygonById(target.dataItem.id);if(polygon){target.disabled=false;returnpolygon['visual'+adapter.slice(0,1).toUpperCase()+adapter.slice(1)];}else{target.disabled=true;}returnvalue;});});//初期化varinitialData=getData(0,0);polygonSeries.data=initialData;bubbleSeries.data=initialData;circle.fill=am4core.color(columnColors[0]);circleHeatRule.maxValue=getMaxValue(0);//ドロップダウンの設定vardateController=newkintoneUIComponent.Dropdown({items:event.records.map(function(record,index){return{label:record[dateCode].value,value:index}}),value:0});varcolumnController=newkintoneUIComponent.Dropdown({items:columnCodes.map(function(tableColumnCode,index){return{label:tableColumnCode,value:index}}),value:0});dateController.on('change',function(value){vardata=getData(value,columnController.getValue());polygonSeries.data=data;bubbleSeries.data=data;});columnController.on('change',function(value){vardata=getData(dateController.getValue(),value);polygonSeries.data=data;bubbleSeries.data=data;circle.fill=am4core.color(columnColors[value]);//円の色を設定circleHeatRule.maxValue=getMaxValue(value);//maxValueを設定});document.getElementById(chartControllersDomId).appendChild(dateController.render());document.getElementById(chartControllersDomId).appendChild(columnController.render());});})();