Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div id="root"></div>
              
            
!

CSS

              
                .refine{
  margin:25px;
}
.yubi{
cursor: pointer;
cursor: hand;
}
.center{
  text-align: center;
}
.tweetText{
  margin:5px;
  border: 1px solid #aaa;
  text-align: center;
  word-break: normal;
  padding: 20px 40px;
}
.centering{
   display: inline-block;
  text-align: left;
}
.result{
  border: solid 0.5px #ccc;
  margin:20px 75px;
}
              
            
!

JS

              
                class ChoroplethMap extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      keyword: "",
      tweetSize:"",
      retweetSize:"",
      strText: [],
      filterKeyword: "",
      year: "",
      month: "",
      onlyReply: false,
      andReply: true,
      notReply: false,
      transform : {x:0,y:0,k:1}
    };
    this.zoom = d3.zoom()
      .scaleExtent([1, 40])
      .on('zoom',()=>{
      const {x,y,k} = d3.event.transform
      this.setState({transform:{x,y,k}})
    })
  }
  componentDidMount(){
    d3.select(this.refs.canvas).call(this.zoom)
  }
  render() {
    const width = 960;
    const height = 500;
    const features = this.props.features;
    const baseData = this.props.pinData;

    const data = this.props.data3;
    data.series.sort(function(a, b) {
      if (a.tweetCount[2] > b.tweetCount[2]) {
        return -1;
      } else {
        return 1;
      }
    });
    const series = data.series.filter(series => {
      return series.tweetCount[2] >= 10000;
    });

    const margin = 30;
    const leftMargin = 130;
    const rightMargin = 200;
    const contentWidth = margin * data.labels.length;
    const contentHeight = 900;
    const topMargin = 100;
    const bottomMargin = 100;

    const svgWidth = leftMargin + contentWidth + rightMargin;
    const svgHeight = contentHeight + bottomMargin + topMargin;

    const color = d3.scaleOrdinal(d3.schemeCategory10);

    const dataName = [100000, 50000, 10000];
    const rScale = d3
    .scaleSqrt()
    .domain([0, d3.max(series, item => d3.max(item.tweetCount))])
    .range([0, 30])
    .nice();

    const colorScale = d3
    .scaleSequential()
    .domain([0.1, d3.max(series, item => d3.max(item.retweetCount))])
    .interpolator(d3.interpolateBlues);

    const pinData = baseData.filter((jiro, i) => {
      if (
        jiro.text.includes(this.state.filterKeyword) === true &&
        jiro.date.includes(this.state.year) === true &&
        jiro.date.includes(this.state.month) === true
      ) {
        if (this.state.onlyReply === true) {
          if (jiro.is_reply === "TRUE") {
            return true;
          } else {
            return false;
          }
        }
        if (this.state.andReply === false) {
          if (jiro.is_reply === "TRUE") {
            return false;
          }
        }
        return true;
      }
    });
    // const [x,y] = projection (mydata[0])

    const projection = d3
    .geoMercator()
    .scale(1000)
    .center([139.69167, 35.68944]);

    const en = pinData.map(a => {
      if (isNaN(projection([a.long, a.lat])[0])) {
        console.log(a);
      }
      return projection([a.long, a.lat]);
    });

    // const mydata = pinData.map(([x,y])=>projection([x,y]))

    const path = d3.geoPath().projection(projection);
    const densityPath = d3.geoPath();

    const density = d3
    .contourDensity()
    .bandwidth(5)
    .cellSize(4)
    .thresholds(100);

    const densityColor = d3
    .scaleSequential()
    .domain([0, d3.max(density(en), d => d.value)])
    .interpolator(d3.interpolateWarm);

    const textPrint = [];

    const zoom = d3.zoom()

    return (
      /*ここから*/
      <div>
        <div>
          <section className="section">
            <div className="container">
              <div className="content has-text-centered">
                <h3>2019年度尾上ゼミ 情報科学研究</h3>
                <h1>
                  福島第一原発事故に関するツイートの時間的地理的分布の可視化
                </h1>
                <h4 className="up">図の説明</h4>
                <p align="left" className="new_line">図は2つ作成し、1つに合併した。<br />
                1つ目は興味に関する図で円の大きさや色の濃さで興味の移り変わりを示している。<br />
                2つ目は位置に関する図で日本地図上に円を配置することで位置の移り変わりを示している。<br />
                1→2にかけて一部の情報を連動できるようにしている。</p>
                <h4 className="up">関連研究の概要</h4>
                <p align="left" className="new_line">今回参考にした坪倉らの研究(Twitter use in scientific communication revealed by visualization of information spreading by influencers within half a year after the Fukushima Daiichi nuclear power plant accident)は、福島第一原発事故発生から6か月間の放射線や原発事故に関連するキーワードを含むツイートとリツイートの合わせて約2500万件を購入した。その中でリツイートは約半数(49.7%)を占めていた。また少数の影響力の強いアカウント(以降はインフルエンサー)のツイートがリツイートされることが多いことが判明した。またこのインフルエンサーのツイートの内容を解析したところ3つの内容に分別できた。事故直後は3つの内容が同等にリツイートされていたが、1か月後には1つの内容が多くされるようになった。ここから政府や国際機関は国民に対してどのように情報発信がなされるべきか重要な示唆を与えていると考えられるという内容の研究であった。
                </p>
                <h4 className="up">データの説明</h4>
                <p align="left" className="new_line">
                  今回の研究では坪倉らの関連研究で用いたキーワード選定を基に6年6か月文のツイートを選定し使用した。<br />
                  【共通して使用する情報】<br />
                  ・放射線に関係するキーワードを含むツイート:関連研究のキーワードを使用<br />
                  ・年月(時間):6年6か月分<br />
                  【1つ目の図】<br />・ツイート数:1か月ごとの合計ツイート数をキーワードごとに収集<br />・リツイート数:1か月ごとの合計リツイート数をキーワードごとに収集<br />
                  【2つ目の図】<br />・位置:そのtweetのした位置、緯度経度<br />・本文:tweetの本文、呟いた内容<br />・リプライか否か:そのtweetが人に対する返信(リプライ)であるか否か<br />・日付:何年何月にtweetされたものであるか
                </p>
               
                <h4>可視化の説明</h4>
                <p align="left" className="new_line">
                  1つ目の図:円の大きさはツイート数、円の色の濃さはリツイート数、縦軸はキーワード、横軸は年月、円をクリックすることで図の下に詳細が表示され、図の上でzoom&panが可能である。<br />
                  2つ目の図:クリックすることで図の下に周辺の円が持つツイートの本文が見れる。図の下のからキーワード、年月、リプライであるか否かが絞り込める。<br />
                  1→2への連動:1つ目の図の円をクリックすると2つ目の図の絞り込みに連動される。
                </p>

              </div>
            </div>
          </section>
        </div>

        <div className="result">
          <div>

            <svg style = {{cursor:'move'}}ref = 'canvas'viewBox={`0,0,${svgWidth},${svgHeight}`}>
              <g transform = {`translate(${this.state.transform.x},${this.state.transform.y})scale(${this.state.transform.k})`}>
                <g transform={`translate(${leftMargin} , ${topMargin})`}>
                  {
                    <g>
                      <line
                        x1="0"
                        y1={topMargin - 5}
                        x2={svgWidth - 200}
                        y2={topMargin - 5}
                        stroke="gray"
                        />
                    </g>
                  }

                  {/*縦軸*/
                    data.labels.map((item, i) => {
                      const date = new Date(item);
                      return (
                        <g key={i} transform={`translate(${32 * i},0)`}>
                          <text y={topMargin - 8} textAnchor="middle" fontSize={8}>
                            {date.getFullYear()}
                            {(date.getMonth() + 1).toString().padStart(2, "0")}
                          </text>
                          <line
                            x1="0"
                            y1={topMargin - 5}
                            x2="0"
                            y2={contentHeight + 45}
                            stroke="gray"
                            />
                        </g>
                      );
                    })}

                  {/*横軸*/
                    series.map((item, i) => {
                      return (
                        <g key={i} transform={`translate(0,${30 * (i + 1) + 15})`}>
                          <text x="-8" y={topMargin - 5} textAnchor="end">
                            {item.keyword}
                          </text>
                          <line
                            x1="0"
                            y1={topMargin - 10}
                            x2={svgWidth - 205}
                            y2={topMargin - 10}
                            stroke="black"
                            />
                        </g>
                      );
                    })}

                  {/*説明用の円*/
                    <g transform={'translate(0,-50)'}>
                      <g transform={`translate( ${svgWidth/4},${0})`}>
                        <text textAnchor='end' fontSize='30'>リツイート数</text>
                        {dataName.map((item, i) => {
                          return (
                            <g transform={`translate( ${i * 150},${0})`}>
                              <circle
                                cx="50"
                                cy='0'
                                r="20"
                                opacity="0.8"
                                fill={colorScale(item)}
                                />
                              <text
                                x="50"
                                y='50'
                                textBaseline="bottom"
                                fontSize="30"
                                textAnchor="middle"
                                >
                                {item}
                              </text>
                            </g>
                          )})
                        }
                      </g>
                      <g transform={`translate( ${svgWidth/2},${0})`}>
                        <text textAnchor='end' fontSize='30'>ツイート数</text>
                        {dataName.map((item, i) => {
                          return (
                            <g transform={`translate( ${i * 150},${0})`}>
                              <circle
                                cx="50"
                                cy="0"
                                r={rScale(item)}
                                opacity="0.8"
                                fill="White"
                                stroke="black"
                                />
                              <text
                                x="50"
                                y='50'
                                textBaseline="bottom"
                                fontSize="30"
                                textAnchor="middle">
                                {item}
                              </text>
                            </g>
                          )})
                        }
                      </g> 
                    </g>
                  }
                  {/*円*/
                    series.map((item, i) => {
                      return (
                        <g key={i} className="yubi">
                          {item.tweetCount.map((n, j) => {
                            return (
                              <g
                                key={j}
                                transform={`translate(${32 * j},${30 * (i + 1)+105} )`}
                                onClick={() => {
                                  this.setState({
                                    filterKeyword: series[i].keyword,
                                    tweetSize:series[i].tweetCount[j],
                                    retweetSize:series[i].retweetCount[j],
                                    keyword: series[i].keyword,
                                    year: data.labels[j].slice(0, 4),
                                    month: data.labels[j].slice(4, 9)
                                  });
                                }}
                                >
                                <circle 
                                  cx="0"
                                  cy="0"
                                  r={rScale(n)}
                                  stroke="gray"
                                  opacity="0.8"
                                  fill={colorScale(item.retweetCount[j])}
                                  />
                              </g>
                            );
                          })}
                        </g>
                      );
                    })}
                </g>
              </g>
            </svg>
            {this.state.keyword && (
              <p className="result" align="center">
                上図で選択した場所<br />
                {this.state.year}年{this.state.month.slice(1,2)}月の{this.state.keyword}<br />
                ツイート数:{this.state.tweetSize}件 , リツイート数:{this.state.retweetSize}件
              </p>
            )}
          </div>

          <div>
            <svg
              width={width}
              height ={height}
              onClick={e => {
                const { offsetX: mouseX, offsetY: mouseY } = e.nativeEvent;
                console.log(mouseX, mouseY);
                en.map(([x, y], i) => {
                  if (
                    Math.sqrt(
                      Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2)
                    ) <= 15
                  ) {
                    textPrint.push("●" + pinData[i].text + "\n");
                  }
                });
                this.setState({ strText: textPrint });
              }}
              >
              <g>
                <g>
                  {features.map((feature, i) => (
                    <path
                      key={i}
                      d={path(feature)}
                      fill="#99cc66"
                      stroke="white"
                      />
                  ))}
                  <g>
                    {density(en).map((feat, k) => {
                      return (
                        <g key={k}>
                          <path
                            d={densityPath(feat)}
                            stroke="#ccc"
                            fill={densityColor(feat.value)}
                            opacity="0.2"
                            />
                        </g>
                      );
                    })}
                    {en.map(([x, y], j) => {
                      if (isNaN(x) || isNaN(y)) {
                        console.log(x, y, j);
                      }
                      // const [x,y] =projection(pin)
                      return (
                        <g
                          key={j}
                          onClick={() => {
                            console.log(x, y);
                            // this.setState({strText:textPrint})
                          }}
                          >
                          <circle
                            cx={x}
                            cy={y}
                            r="1"
                            fill="blue"
                            opacity="0.5"
                            />
                        </g>
                      );
                    })}
                  </g>
                </g>
              </g>
            </svg>

            <div className="center">
              <div>
                <div className="refine">
                  <form
                    onSubmit={event => {
                      event.preventDefault();
                      this.setState({ filterKeyword: this.refs.taro.value });
                    }}
                    >
                    <input ref="taro" />
                    <button>絞り込み</button>
                  </form>
                </div>
                <div className="refine">
                  <div>
                    <b>年 : </b>
                    <select
                      name="name"
                      id="name"
                      value={this.state.year}
                      onChange={event => {
                        this.setState({ year: event.target.value });
                      }}
                      >
                      <option value="">ALL</option>
                      <option value="2011">2011</option>
                      <option value="2012">2012</option>
                      <option value="2013">2013</option>
                      <option value="2014">2014</option>
                      <option value="2015">2015</option>
                      <option value="2016">2016</option>
                      <option value="2017">2017</option>
                    </select>
                    <b> 月 : </b>
                    <select
                      name="name"
                      id="name"
                      value={this.state.month}
                      onChange={event => {
                        this.setState({ month: event.target.value });
                      }}
                      >
                      <option value="">ALL</option>
                      <option value="/1/1 ">1</option>
                      <option value="/2/1 ">2</option>
                      <option value="/3/1 ">3</option>
                      <option value="/4/1 ">4</option>
                      <option value="/5/1 ">5</option>
                      <option value="/6/1 ">6</option>
                      <option value="/7/1 ">7</option>
                      <option value="/8/1 ">8</option>
                      <option value="/9/1 ">9</option>
                      <option value="/10/1">10</option>
                      <option value="/11/1">11</option>
                      <option value="/12/1">12</option>
                    </select>
                  </div>
                  <div>
                    <b>表示件数 : {pinData.length} 件</b>
                  </div>
                  <div>
                    <b>現在の検索ワード : {this.state.filterKeyword}</b>
                  </div>
                  <div>
                    <div
                      onClick={() => {
                        if (this.state.onlyReply === false) {
                          this.setState({
                            onlyReply: true,
                            andReply: false,
                            notReply: false
                          });
                        }
                      }}
                      >
                      <input
                        type="checkbox"
                        checked={this.state.onlyReply}
                        readOnly
                        />
                      <label>リプライのみを表示する</label>
                    </div>
                    <div
                      onClick={() => {
                        if (this.state.notReply === false) {
                          this.setState({ notReply: true,onlyReply: false,andReply: false });
                        }
                      }}
                      >
                      <input
                        type="checkbox"
                        checked={this.state.notReply}
                        readOnly
                        />
                      <label>リプライのみ表示しない</label>
                    </div>
                    <div
                      onClick={() => {
                        if (this.state.andReply === false) {
                          this.setState({ andReply: true,onlyReply: false,notReply: false });
                        }
                      }}
                      >
                      <input
                        type="checkbox"
                        checked={this.state.andReply}
                        readOnly
                        />
                      <label>ツイートを全て表示する</label>
                    </div>
                  </div>
                </div>
              </div>
              <div>
                <button
                  onClick={() => {
                    this.setState({ andReply: true,
                                   onlyReply: false,
                                   notReply: false,
                                   month: "",
                                   year: "",
                                   filterKeyword: ""  });
                  }}
                  >
                  条件を初期値にする
                </button>
              </div>
              <div className="center">Tweet information</div>
              <div className="tweetText" style={{ whiteSpace: "pre-line" }}>
                <p align="left">
                  ({this.state.strText.length})<br />
                  {this.state.strText}
                </p>
              </div>
            </div>
          </div>
        </div>
        <section className="section">
          <div className="container">
            <div className="content has-text-centered">
              <h4>1,2の図を別に見たときの考察</h4>
              <p align="left" className="new_line">
                1つ目の図:2011年3月・4月ツイート数が多いかつ、リツイート数も多いものが、多く存在した。「放射」が両法の反応が一番多い。次に多い「原発・放射・福島」と同時につぶやかれていました。
                「水」・「東京電力」・「東電」などの反応も大きい。「米」や「避難」などは反応が低く、身近な物事よりも直視しづらい出来事に人々は興味を持っていると考えられる。
                また2013年の9月を見てみると、一度落ち着いたワードである水や汚染といったワードが再び浮かび上がってきます。
                例えばこの時だと原発のタンクから汚染水が漏洩した問題が起こった時期であり、その影響が図に表れているのがわかります。
                同じような例が2014年でもあり被爆や福島といったワードが今度は浮かび上がってきます、これは
                「美味しんぼ」という漫画において「鼻血は被爆が原因」と明記したことに事実かどうか等で話題にあがりました。
                こういった話題によってふたたび人々の関心をあつめていることがわかります。
                そしてある間隔ツイート数が多くなる時があります。これはメディアが過去の振り返りを取り上げていて、人々がそれについて反応をしていました。しかし徐々にツイート数が減っており、リツート数はさらに減っていることがわかります。人々の関心が徐々に薄れていっているのだと考えられます。
              </p>
              <p align="left" className="new_line">
                2つ目の図:絞り込みをしない状態では関東でのtweetが多いだけでなく大阪付近や福島も多いのが確認できる。これは人口の多い場所であるのと事故が発生した場所であることが関係していると考えられる。<br />時間で区切った時を見てみると事故が発生した月は福島でのtweetも多く避難場所の共有や放射線の情報の共有などを行っている。1年や2年経つとtweet数が減っていくことも確認できる。これは十分な情報が流れていたり立入禁止区域になっていることも要因として考えられます。<br />リプライのみを表示したときを見てみると、含まないときと比べてtweetの場所が限られていることがわかります。これは今回の集めたキーワードを含むやり取りがされている場所が限られているという意味を表しており、情報収集に役立っていると考えられます。
              </p>
              <h4>両方を見たときの考察</h4>
              <p align="left" className="new_line">
                位置情報を含むツイートが少なく、リツイートなどの関心と合わない部分もあったが概ね一致した。また盛り上がるタイミングでオリンピックの決定や原発再稼働のデモ活動など社会的背景と結びつけることができたため有意義な可視化になったと考えられる。
              </p>
            </div>
          </div>
        </section>
        <div className="result">
          <p>
            1つ目の図のキーワード<br />放射,被ばく,被曝,被爆,除染,線量,ヨウ素,セシウム,シーベルト,Sv,mSV,μSV,uSV,msv,μsv,usv,ベクレル,Bq,ガンマ線,γ線,核種,甲状腺,甲状線,チェルノブイリ,規制値,基準値,学会,警戒区域,避難区域,産科婦人科,周産期・新生児医,日本疫,核医,電力中央,学術会議,環境疫,物理学会,プルトニウム,ストロンチウム,暫定基準,暫定規制,屋内退避,金町浄水場,出荷制限,管理区域,避難地域,モニタリング,スクリーニング,ホットスポット,汚染,(検査 (食品,水,土)),(リスク (がん,ガン,癌)),(影響 (妊婦,妊娠,出産,子ども,子供,こども,児)),母子避難,避難弱者,自主避難,避難関連死,避難死,((福島,ふくしま,フクシマ) (避難,米,野菜,牛肉,食品,産,安全,安心,不安,検査)),サーベイメータ,半減期,遮蔽,疫学,ICRP,IAEA,WHO,コーデックス委員会,ECRR,JCO事故,東海村事故,東海村臨界,臨界事故,(検査 (野菜,山野草,魚)),東電,東京電力,安全委,保安院,規制庁,規制委,安全厨,危険厨,廃炉,メルトダウン,吉田調書,再稼働,反原発,御用学者,アイソトープ,同位体,同位元素,いちえふ,第五福竜,ビキニ事件,ビキニ事故,死の灰,風評,UNSCEAR,((原発,原子力,福島,ふくしま,フクシマ,避難) + 健康),((福島,ふくしま,フクシマ,検査) + きのこ),((福島,ふくしま,フクシマ) + 過剰 + (診断,治療,診療)),((原発,原子力,福島,ふくしま,フクシマ) + (日テレ,TBS,TBS,フジ,朝日,テレ朝,NHK,NHK,NEWS,News,news,NEWS,News,news,ニュース,バンキシャ,Nスタ,Nスタ,報道,サンデーモーニング,クローズアップ,クロ現,古舘,古館,関口,宮根,池上彰,読売,毎日,産経,テレビ,番組,新聞,報道,マスコミ,メディア,民放,民報,民友,放送,FM,FM,ラジオ,通信)
          </p>
        </div>
        <div className="result">
          <p>
            2つ目の図のキーワード<br />放射,被ばく,被曝,被爆,除染,線量,ヨウ素,セシウム,シーベルト,Sv,mSV,μSV,uSV,msv,μsv,usv,ベクレル,Bq,ガンマ線,γ線,核種,甲状腺,甲状線,チェルノブイリ,規制値,基準値,学会,警戒区域,避難区域,産科婦人科,周産期,新生児医,日本疫,核医,電力中央,学術会議,環境疫,物理学会,プルトニウム,ストロンチウム,暫定基準,暫定規制,屋内退避,金町浄水場,出荷制限,管理区域,避難地域,モニタリング,スクリーニング,ホットスポット,汚染,(検査(食品,水,土)),(リスク(がん,ガン,癌)),(影響(妊婦,妊娠,出産,子ども,子供,こども,児)),母子避難,避難弱者,自主避難,避難関連死,避難死,((福島,ふくしま,フクシマ)(避難,米,野菜,牛肉,食品,産,安全,安心,不安,検査)),サーベイメータ,半減期,遮蔽,疫学,ICRP,IAEA,WHO,コーデックス委員会,ECRR,JCO事故,東海村事故,東海村臨界,臨界事故,(検査(野菜,山野草,魚)),東電,東京電力,安全委,保安院,規制庁,規制委,安全厨,危険厨,廃炉,メルトダウン,吉田調書,再稼働,反原発,御用学者,アイソトープ,同位体,同位元素,いちえふ,第五福竜,ビキニ事件,ビキニ事故,死の灰,風評
          </p>
        </div>
        <footer className="footer">
          <div className="content has-text-centered">
            <p>&copy;2019 田中大道</p>
          </div>
        </footer>
      </div>
    );
  }
}
const App = ({ data, pinData, data3 }) => {
  const { features } = topojson.feature(data, data.objects.japan);
  return <ChoroplethMap features={features} pinData={pinData} data3={data3} />;
};

(async () => {
  const response = await fetch(
    "https://raw.githubusercontent.com/raccoonfox03/HRMC_twitter_1/master/Twitter_data/japan_10over2.json"
  );
  const data = await response.json();
  // const response2 = await fetch('https://raw.githubusercontent.com/raccoonfox03/HRMC_twitter_1/master/Twitter_data/convertcsv1.json')
  const response2 = await fetch(
    "https://raw.githubusercontent.com/raccoonfox03/HRMC_twitter_1/master/Twitter_data/japan_square.json"
  );
  const pinData = await response2.json();
  const response3 = await fetch(
    "https://raw.githubusercontent.com/raccoonfox03/HRMC_twitter_1/master/Twitter_data/3plus4.json"
  );
  const data3 = await response3.json();

  ReactDOM.render(
    <App data={data} pinData={pinData} data3={data3} />,
    document.getElementById("root")
  );
})();

//const [x,y]= projection ([139.633, 35.661],[127.6865876,26.20681017])

//color(feature.properties.value) //地図色

//fill={densityColor(feat.value)} //等高線色

// .thresholds(10)
//   .bandwidth(1)
//   .cellSize(1)

              
            
!
999px

Console