cb37の日記

地理情報系大学院生

2022年を振り返って

今年1年の振り返り

せっかくブログ始めたのでここらで振り返りをしようと思います.(下書きばかりで公開できてませんが)

時系列で簡単に行きます.

1~3月

 3月の学会に向けて,ひたすら論文書いたり実験してました.この時期は正直研究以外してないです.

 学会当日は鋭い質問にどぎまぎしながら答えた記憶があります.当然,嘘偽りなく答えたのですが,「できないことをできない」とか「考えていなかったことを考えていなかった」で済ませるのではなく,「今はできないが,こうすればできる」や「その考えなら,こういう考えもできますよね」みたいな一歩踏み込んだ答え方ができるといいんだろうな〜というのを強く感じました.この辺はここから普通のミーティングで実践するように意識してました.

 あとは周りの発表聞いたりしましたが,かなりレベル高いな〜と思いました.特に難しいことやってるなと思う人は大学院生だったりしました.今は分かりますが,学部生と大学院生だとやっぱり研究する力が違うんだろうなと思います.ましてや座長や司会はこの道何年のプロと考えると鋭い質問が来るのはあたりまえなんですよね.

 大学院進学時点では研究職にも少し興味ありましたが,自分の仕事が社会の役に立つ実感が湧かないことが決め手で,研究職はないなと思いました.研究職に就いたとしても,社会に成果を還元しやすい企業の研究職ぐらいかなと思います.少なくとも博士課程に進みたくないと思いました.

4~6月

 6月末の国際会議への論文投稿を目指して研究を始めました.3月に発表した内容とはほぼ異なる内容で研究を進めました.3ヶ月で実験から英語論文の執筆まで行うという,メンタルがかなりキツかったのをよく覚えています.特に夏のインターンはこのタイミングで申し込みとかを行うので,周りに遅れを取って焦っていたのを覚えています.実際日程的に応募を辞退した企業もありました.

 あとは給付型の奨学金の申請をして,そのための面接なんかもあったりしました.結果としてこの奨学金の奨学生に採用されました.正直研究ばかりの生活で,アルバイトをする時間なんかないので,こちらの奨学金制度のおかげでなんとか生活できています.感謝.

7~9月

 インターンに参加してました.これに関しては別記事に体験記があります.やはり自分は研究職ではなくエンジニアがいいなと確信した体験でもありました.学ぶことの多いインターンでした.

10~11月

 結果は本会議ではなくワークショップでの採録でした.この期間はそのための学会発表の準備と本番でした.パスポートの手続きや発表する原稿の準備なんかもしてました.割とバタバタとしていました.

 発表はフライトなんかは問題なかったのですが,行った国の円安,物価高のせいでめちゃくちゃ金欠になりました.学校から交通費は丸々支給され,宿泊費も定額支援だったのですが,食費やお土産代(それは仕方ない)なんかで出費がものすごかったです.来年も国際会議で発表したいとは思いつつ,普通に金欠で行けない説があります.

 発表に関しては周りはほぼ研究者ばかりでした.ポスター発表では学生がいましたが,ワークショップの本会議でも発表してるのは教授とかばかりの印象でした.やはり内容は難しく,英語ももっと勉強する必要があるなと思いました.発表に対する質疑応答も何度も聞き返して何とか答えてました.

 月末は学校の TA のバイトを引き受けたので,学部生の実験の補助をしてました.自分は受けてない実験だったので事前に資料を頂いて勉強したりしたのですが,割と難しくて焦りました.たださすがに学部生に比べると理解は進みますし,質問に対してもちゃんと答えられました(それはそう).ただ教え方も答えをただ教えるのではなく,どのようにすれば答えに近づくかといったことを意識して教えるようにしました.正直,2, 3年で自分が成長した自信はあまりなかったのですが,ちゃんと TA ができたということは学部生のときの自分よりは成長できているんだと思って安心しました.

12月

 就活を始めました.まだ初めたばかりなので内定は出ていません.これから就活が本格的に始まります.頑張ります.

来年度の抱負

ゆとりある生活をする

割と自分はやりたいことややるべきことを一気に熱中して進めてしまいます.無理をしている実感はないのですが,若いが故に体力があるだけ作業してしまう節があります.(若い内に無理しとこうみたいな考えもあるんですが)

今はいいんですが,これから体力がなくなっても大丈夫なように,余力をもって生活していきたいです.

株式会社いい生活 2022 サマーインターンシップ体験記

概要

株式会社いい生活様のサマーインターンシップに参加させていただきました.結構時間がたってしまったので,詳細な内容までは触れられないかもしれないですが,学ぶことの多い機会となり,自分の将来の方向性を決める体験になりました.

ちなみに自分がいい生活のインターンに応募しようと思ったのは,先輩が参加していたからです.一学年上の先輩も,二学年上の先輩も行っており,おすすめされたのがきっかけでした.ただ,その後は自分の研究分野である地理情報に通ずるものもあり,興味を持ち参加しました.

事前

インターン期間中に使用する git や Node.js などの環境構築をします.分からないところを質問して教えていただきました.他の方の質問にもすぐに答えていたのが印象的でした. 書くほどではないかと思いましたが,個人的に環境構築力はエンジニア力だと思っているので,よい環境でインターンができるなと思っていました.

また,事前学習用の資料も頂きました.こういうのがあるとインターン参加のハードルが下がるので,大変ありがたかったです.

内容

いい生活の API を使った チームでの Web サービス開発でした.インターンは全てオンラインで行われ,チームは基本4人(自分は3人)で,どういったものを作るか,実装,発表,反省まで,現場のものづくりに近い体験をさせていただきました.

技術的には TypeScript, Vue.js, Firebase, GitLab などを使いました.基本的には Gather で常時接続しつつ,分からないところがあればメンターの方やチームメンバーに質問する形で進めました.資料があらかじめ用意されており,この資料が感動するレベルで分かりやすかったです.また,チームに強い方がいたのでその方の話を参考にして進めました.ここら辺はチーム開発ならではの感覚でした.

実装時間の合間に,社員の方のお話を伺う機会も用意して頂いていました.エンジニア以外の方のお話も聞けて大変参考になりました.不動産 Tech という,なかなか馴染みのない業界でしたが,興味深い話をたくさん聞けて良かったです.

自分が具体的に担当した内容としては,Firebase を使ったログイン機能の実装でした.Firebase はどんな機能があるか程度の知識しかなかったのですが,手探りで少しずつ進めました.ログイン画面も実装しましたが,中々センスのない感じになってしまったのが心残りです.あとは地図の機能の実装ができなかったのが心残りだったので軽くやってみたのがこちらの記事 になります.

あとは GitLab を使ったチーム開発がいい経験になりました.Issue を立て,Project でそれを管理し,レビューをお願いし,マージするという一連の流れを体験し, バージョン管理システムの有効性を実感しました.個人でもこの辺り使えた方がいいと思うので使っていこうと思います.

学んだこと

学んだことは主に次の3つです.

  • エンジニアの働き方
  • チーム開発
  • いい生活について

エンジニアがどのような働き方なのか,漠然としていたものが少し明確になりました.どれだけ個人で勉強をしていても,やはり業務とは異なると思うので,こういった機会は本当に参考になりました.また,チーム開発の経験もあまりなかったため,いい経験になりました.分からないところは聞くというのは分かっていても「もう少し自分でやった方がいいのではないか」と思いがちなので,そこら辺は適度に聞くべきだと思い,質問の仕方も大事であると気づきました.インターン中に社員の方のお話を聞く機会が多くあったので,いい生活や不動産 Tech について知ることができました.なかなか他の分野ではないであろう話を聞くことができて面白かったです.質問をする機会も多かったり, Slack の雰囲気からも,いい生活の働きやすい社風を感じることができました.

感想

いい生活の雰囲気が非常によく,働きやすそうだなと思いました.また,チーム開発も楽しかったです. 貴重な体験ができて日当ももらえるという,本当にメリットしかないのでエンジニアに興味がある人におすすめです.

Mapbox + Vue.js + OpenStreetMap で道路ネットワークグラフの描画

概要

以前某インターンで Vue.js を初めて使いました.自分の専門が地理情報関係なので, Vue.js と地図ライブラリを使って簡単なアプリを作ってみようと思いました.地図ライブラリには Mapbox を採用しました. Mapbox は自由度が非常に高く,使いこなせるようになりたいと思っているためです.

何をしようかと考えたのですが,Mapbox の長所は先ほど述べた通りの自由度の高さと描画できる数の多さだと思っているので,たくさん描画できるものにしようと思いました.そこで, OpenStreetMap が提供している道路ネットワークグラフにしました.

環境構築はこちら の通りでした.

実装

道路ネットワークグラフの取得

道路ネットワークグラフは OSMnx を使う方法 で取得しました.今回は新宿区のデータを使用します. 自分は既に環境構築していたのですが,苦戦した記憶があります.ただ,これはかなり便利なのでおすすめです.

道路ネットワークグラフの geojson への変換

取得した道路ネットワークグラフの csv を geojson へ変換します.以前の記事では csv2geojson というものを使いましたが,今回は自力で変換してみました.割と力業でなんとかした感がありますが,最後にコードを残しておきます.

Vue.js で道路ネットワークグラフの描画

ただグラフを描画するだけでは面白くないため,別の場所から新宿駅へスクロールするボタンをつけます.UI フレームワークには Element Plus を使用しました.

Element UI と Element Plus を間違えたり,main.ts の app.use のあたりで沼ったりしました.

グラフの描画はこちら を参考にして作成しました.data のところに先ほど変換した geojson に指定して完成です.

Vue で作成したディレクトリには assets というのがあって,そこに geojson を入れていたのですが,ここではなく public に入れる,というところでまた詰まってました.

完成

道路ネットワークグラフはピンクで表示しています.マップを移動させ後にボタンを押すと新宿駅が中心になるように移動します.ほぼ SPA になったのですが,これを基本にしていけばもっといろいろできるので試していこうと思います.

コード

MyMap.vue

<script setup lang="ts">
import { onMounted, ref } from "vue";
import mapboxgl from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";

const visibility = ref("visible");

mapboxgl.accessToken =
  "API key";

let mapObj;
onMounted(() => {
  mapObj = new mapboxgl.Map({
    container: "japan-shinjuku", // container ID
    style: "mapbox://styles/mapbox/streets-v11", // style URL
    center: [139.723845, 35.654858], // starting position [lng, lat]
    // center: [-122.486052, 37.830348],
    zoom: 14, // starting zoom
  });

  mapObj.on("load", () => {
    mapObj.addSource("road-network", {
      type: "geojson",
      data: "/road_network_edges.geojson",
    });
    mapObj.addLayer({
      id: "road-network",
      type: "line",
      source: "road-network",
      layout: {
        "line-join": "round",
        "line-cap": "round",
        visibility: visibility.value,
      },
      paint: {
        "line-color": "#ff00ff",
        "line-width": 4,
      },
    });
  });
});

const toShinjuku = (e: Event) => {
  mapObj.flyTo({
    center: [139.7, 35.689],
    essential: true,
  });
};
</script>

<template>
  <div id="japan-shinjuku" :style="{ height: '60em' }"></div>
  <div class="buttons">
    <el-row class="mb-4">
      <el-button type="primary" @click="toShinjuku"
        >Move Shinjuku Station</el-button
      >
    </el-row>
  </div>
</template>
<style scoped>
.buttons {
  margin-top: 20px;
  justify-content: center;
  display: flex;
}
</style>

csv2geojson.js

// read csv file and convert to geojson
const fs = require('fs');
const csv = require('csv');
const FOLDER_NAME = '/Shinjuku,Tokyo,Japan';
const FILE_NAME = '/road_network_edges.csv';

function genGeoJson() {
  return new Promise(function (resolve, reject) {

    let geojson = { "type": "FeatureCollection"};
    fs.createReadStream(__dirname + FOLDER_NAME + FILE_NAME)
      .pipe(csv.parse(function (err, data) {
        
          let features = [];
      
          data.forEach((element, index) => {
            if (index == 0) {
              return;
            }
            element.forEach((item) => {
              const result = item.indexOf('LINESTRING');
              if (result !== -1) {
                
                const replacementWords = [
                  ['LINESTRING', ''],
                  [' (', ''],
                  [')', ''],
                  [/, /g, '],['],
                  // [/ /g, ','],
                ]

                replacementWords.forEach((replacementWord) => {
                  item = item.replace(replacementWord[0], replacementWord[1]);
                });
                item = item.split('],[');
                item = item.map((item) => {
                  return item.split(' ').map(Number);
                });
                // console.log(item);

                const obj = {
                  "type": "Feature",
                  "geometry": {
                    "type": "LineString",
                    "coordinates": item
                  },
                  "properties": {}
                };
                features.push(obj);
              }
            })
          });
        geojson['features'] = features;
        resolve(geojson);
      }));
  });
}

genGeoJson().then(function (geojson) {
  fs.writeFileSync(__dirname + FOLDER_NAME + '/road_network_edges.geojson', JSON.stringify(geojson, null, 2));
  console.log('done');
});

Mapbox で街路灯のヒートマップを作る

概要

自分は基本的に地図ライブラリに JavaScript の Leaflet か,それの Python バージョンの folium を使っています.簡単な描画であればこれが最適だと思いますが,もっと複雑なものを描画したい,大容量のデータを描画したいとなった時,これでは限界があります.そこで Mapbox を使ってみようと思います. Mapbox のすごさが分かるのはこちら

練習用なので何でもよかったんですが,街路灯のヒートマップを作ろうと思います.街路灯データはこちら

実装

csv から geojson への変換

今回は簡単のために pbf ではなく, geojson を使います.上記で提供されているデータは csv 形式なのでこれをまず geojson に変換します.geojson への変換は次のツールを使います.日本語が対応していないっぽいですが今回は点だけ使うので文字化けは一旦無視します.

GitHub - mapbox/csv2geojson: magically convert csv files to geojson files

完成

参考は ヒートマップレイヤーの作成 | Mapbox GL JS | ドキュメント | Mapbox (というかほぼ同じ) Mapbox は API キーが必要ですがログインしておけば勝手に入れてくれているのでそのままコピー&ペーストで試せます. データソースを先ほど変換した geojson にし,初期のズームレベルや位置等を変更して出来上がりです. 点の描画とヒートマップの堺がうまくいっていませんが今回はこれで良しとします.

コード

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ヒートマップレイヤーの作成</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>

<script>
   mapboxgl.accessToken = 'APIkey';
    const map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/dark-v10',
        center: [139.6792, 35.7601],
        zoom: 12
    });


    map.on('load', () => {
        // Add a geojson point source.
        // Heatmap layers also work with a vector tile source.
        map.addSource('nighlight', {
            'type': 'geojson',
            'data': 'geo.geojson'
        });

        map.addLayer(
            {
                'id': 'nighlight-heat',
                'type': 'heatmap',
                'source': 'nighlight',
                'maxzoom': 14,
                'paint': {
                    // Increase the heatmap weight based on frequency and property magnitude
                    'heatmap-weight': [
                        'interpolate',
                        ['linear'],
                        ['get', 'mag'],
                        0,
                        0,
                        6,
                        1
                    ],
                    // Increase the heatmap color weight weight by zoom level
                    // heatmap-intensity is a multiplier on top of heatmap-weight
                    'heatmap-intensity': [
                        'interpolate',
                        ['linear'],
                        ['zoom'],
                        0,
                        1,
                        14,
                        3
                    ],
                    // Color ramp for heatmap.  Domain is 0 (low) to 1 (high).
                    // Begin color ramp at 0-stop with a 0-transparancy color
                    // to create a blur-like effect.
                    'heatmap-color': [
                        'interpolate',
                        ['linear'],
                        ['heatmap-density'],
                        0,
                        'rgba(33,102,172,0)',
                        0.2,
                        'rgb(103,169,207)',
                        0.4,
                        'rgb(209,229,240)',
                        0.6,
                        'rgb(253,219,199)',
                        0.8,
                        'rgb(239,138,98)',
                        1,
                        'rgb(178,24,43)'
                    ],
                    // Adjust the heatmap radius by zoom level
                    'heatmap-radius': [
                        'interpolate',
                        ['linear'],
                        ['zoom'],
                        0,
                        2,
                        14,
                        20
                    ],
                    // Transition from heatmap to circle layer by zoom level
                    'heatmap-opacity': [
                        'interpolate',
                        ['linear'],
                        ['zoom'],
                        12,
                        1,
                        14,
                        0
                    ]
                }
            },
            'waterway-label'
        );

        map.addLayer(
            {
                'id': 'nighlight-point',
                'type': 'circle',
                'source': 'nighlight',
                'minzoom': 12,
                'paint': {
                    // Size circle radius by earthquake magnitude and zoom level
                    'circle-radius': [
                        'interpolate',
                        ['linear'],
                        ['zoom'],
                        12,
                        ['interpolate', ['linear'], ['get', 'mag'], 1, 1, 6, 4],
                        16,
                        ['interpolate', ['linear'], ['get', 'mag'], 1, 5, 6, 50]
                    ],
                    // Color circle by earthquake magnitude
                    'circle-color': [
                        'interpolate',
                        ['linear'],
                        ['get', 'mag'],
                        1,
                        'rgba(33,102,172,0)',
                        2,
                        'rgb(103,169,207)',
                        3,
                        'rgb(209,229,240)',
                        4,
                        'rgb(253,219,199)',
                        5,
                        'rgb(239,138,98)',
                        6,
                        'rgb(178,24,43)'
                    ],
                    'circle-stroke-color': 'white',
                    'circle-stroke-width': 1,
                    // Transition from heatmap to circle layer by zoom level
                    'circle-opacity': [
                        'interpolate',
                        ['linear'],
                        ['zoom'],
                        12,
                        0,
                        13,
                        1
                    ]
                }
            },
            'waterway-label'
        );
    });
</script>

</body>
</html>

大量の地理情報データを扱いたい際に有効なバイナリベクトルタイル

概要

諸事情により大量の地理情報データを扱いたいことがあり,苦戦してしまったのでその備忘録です.地理情報を扱う際に最初に思いつくのは JavaScript の Leaflet かと思いますが,実は大量のデータを扱うことには向いていません.ここで知ってほしいのがバイナリベクトルタイルです.

バイナリベクトルタイルの概要については分かりやすい資料があったのですが,一番分かりやすい資料はどこかに行ってしまいました...

とりあえずこちらの3つが分かりやすいと思います.

使ってみた

実際に使ったのはかなり前なので,メモを元に書きます.自分は geojson から変換しました.変換は tippecanoe というのを使います.インストール方法とかは覚えていませんがそんなに難しくはなかったはず.

tippecanoe をインストールしたら次のようなコマンドで行けます.

tippecanoe --no-tile-compression --no-feature-limit --no-tile-size-limit -z12 -Z0 -e output_dir -l tw /*geojson

参考はこちら なのですが, あまりに大規模なファイルを作成しようとするとエラーがでるので --no-feature-limit --no-tile-size-limit を付けます.

これでズームレベルごとに pbf ファイルができるのでこれで完成です.

Leaflet を使ったWifi スポットの可視化

概要

JavaScriptインタラクティブ地図ライブラリであるLeafletを使ってみたので備忘録です.いつもはPythonのfoliumという,Leafletのhtmlファイルを生成するライブラリで作業しているのですが,諸事情でJavaScriptを使う用事があったので使ってみました.JavaScript自体はNode.jsでゲームを作ったりした経験があるので初めてではないのですが,割と忘れていましたね.

やってみたのはLeafletを使ったWifiスポットの可視化です.ただ地図を表示するだけでは味気ないと思ったからで,特にそこに深い意味はないです.データは130001_東京都_公衆無線LAN - FREE Wi-Fi & TOKYO スポット情報 を使いました.説明は次のようになっています.

外国人旅行者等が多く訪れる都立施設などにおいて無料で利用できる公衆無線LANWi-Fi)サービス「FREE Wi-Fi & TOKYO」のスポット一覧です。 中身を見るとあまり多くのデータが含まれているわけではないです.東京ならカフェや飲食店みたいなところの方が多いかもしれませんが,テストなので丁度いいと思います.

最近はデジタル庁のおかげなのか,少しずつオープンデータが増えてきてうれしい限りです.自分は,データはものづくりにおける材料だと思っています.一つ公開されるだけでも,既存のものとの組み合わせることでさらに面白いものが作れると思っているので,今後もこの方向でいってほしいものです.

実装

csvファイルを読み込むところでエラーが.

Access to XMLHttpRequest at 'file://wsl%24/Ubuntu-20.04/home/{path}/wifispot/freewifiandtokyo.csv' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

こちらで解決でした.【Ajax】ローカルファイルを読み込もうとしたらCORSエラーが発生したので解決した - Qiita

やることは,

  • 場所名が入っているのでピンを立てて名前を出す.
  • また,Wifiの届く範囲を円で示す.

ただ両方だと見づらいのでボタンを付けて切り替えられるようにしました.

コード

<!DOCTYPE html>
<html>
<head>
    <title>Wifi_Spot.html</title>
    <meta charset="utf-8" />
    <link rel = "stylesheet" href = "https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" />
    <script src = "https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>
    <script>
        let csvdata = [];
        let visibleCircle = true;
        let visibleMarker = true;
        var my_map;

        //CSVファイルを読み込む関数getCSV()の定義
        function getCSV(){
            return new Promise(function(resolve, reject){
                var req = new XMLHttpRequest(); // HTTPでファイルを読み込むためのXMLHttpRrequestオブジェクトを生成
                req.open("get", "freewifiandtokyo.csv", true); // アクセスするファイルを指定
                req.overrideMimeType('text/plain; charset=Shift_JIS');
                req.send(null); // HTTPリクエストの発行
                
                // レスポンスが返ってきたらconvertCSVtoArray()を呼ぶ    
                req.onload = function(){
                    convertCSVtoArray(req.responseText); // 渡されるのは読み込んだCSVデータ
                    resolve();
                }
            });
        }
        // 読み込んだCSVデータを二次元配列に変換する関数convertCSVtoArray()の定義
        function convertCSVtoArray(str){ // 読み込んだCSVデータが文字列として渡される
            var result = []; // 最終的な二次元配列を入れるための配列
            var tmp = str.replace("\\r", "");
            tmp = tmp.split("\n"); // 改行を区切り文字として行を要素とした配列を生成

            // 各行ごとにカンマで区切った文字列を要素とした二次元配列を生成
            for(var i=0;i<tmp.length;++i){
                result[i] = tmp[i].split(',');
            }
            csvdata = result;
        }

        // 
        function init() {
            if (my_map != undefined) { my_map.remove(); }
            my_map = L.map('map').setView([35.65809922, 139.74135747], 12);
            mapLink = '<a href="https://openstreetmap.org">OpenStreetMap</a>';
            L.tileLayer(
                'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                    attribution: 'Map data &copy; ' + mapLink,
                    maxZoom: 18
                }).addTo(my_map);

                // 処理
                getCSV().then(function(){

                    for (let i=1; i<csvdata.length-1; i++){
                        if(visibleCircle){
                            L.circle([Number(csvdata[i][3]), Number(csvdata[i][4])], {
                                radius: 500,
                                color: 'blue',
                                fillColor: '#399ade',
                                fillOpacity: 0.3
                                }).addTo(my_map);
                        }
                        if(visibleMarker){
                            L.marker([Number(csvdata[i][3]), Number(csvdata[i][4])]).addTo(my_map).bindTooltip(csvdata[i][1]);
                        }
                    };
                });

        }

        function drawCircle(){
            for (let i=1; i<csvdata.length-1; i++){
                L.circle([Number(csvdata[i][3]), Number(csvdata[i][4])], {
                    radius: 500,
                    color: 'blue',
                    fillColor: '#399ade',
                    fillOpacity: 0.3
                    }).addTo(my_map);
            };
        };

        function drawMarker(){
            for (let i=1; i<csvdata.length-1; i++){
                L.marker([Number(csvdata[i][3]), Number(csvdata[i][4])]).addTo(my_map).bindTooltip(csvdata[i][1]);
            };
        };

        function switchCircle(){
            visibleCircle = !visibleCircle;
            init();
        }

        function switchMarker(){
            visibleMarker = !visibleMarker;
            init();
        }
    </script>
</head>
<body onload="init()">
    <div id="map" style="width: 100%; height: 800px; border: solid 1px"></div>
    <button class="visible circle" type="button" onclick="switchCircle();">Circle</button>
    <button class="visible marker" type="button" onclick="switchMarker();">Marker</button>
</body>
</html>

関連リンク

ブログはじめてみた

筆者は都内の理系大学院生です.周りと少しだけ違う進路を進みながら,悩みながら現在は大学院で研究をしています.私はこの日記を書いている2022年夏時点で修士1年生で,インターンシップなどの就活がはじまりました.ちょうど今までの人生を振り返って,これからを見つめ直すいい機会だと思い,日記を書こうと思いました.日記自体は以前も書きたいと思ったことはあったのですが,いざ書こうと思うと意外と書くことがなくて困ってそのままでした.ただ,今なら多少は誰かの役に立つような日記が書けるのではと思い,色々残しておこうと思いました.(日記なので別に役に立たなくてもいいのでは?というのはありますが私は「自分,もしくは他人にとってプラスになる」ことがモチベーションになるので...)

今のところ書こうと思っているのは,研究を通して培った技術,自分の経験したこととかかなぁと思っています.日記なのでゆったり書きます.割とやると決めたことは長続きするほうなので細々と続けたいと思います.