LoginSignup
1
0

【GAS】Googelスプレッドシートからgeojsonを出力し、MapLibreで表示してみた

Posted at

はじめに

「やりたいこと」を実現するため、いろんな人の情報を参考にし、作成しました。
不備・間違いの指摘やもっと良い方法等があれば、ご教示頂けますと幸いです。

やりたいこと

緯度・経度のついたデータをフォームを用いて収集し、地図表示したい

上記を実現するための具体的な仕組み

Googleフォームに回答した結果(緯度・経度情報あり)をGoogleスプレッドシートに反映し、geojsonに変換。その後、地図ライブラリ(今回はMapLibre)を用い表示した。

収集したデータ例

緯度・経度と日時、数値データが含まれたものを想定。
Googleフォームでの回答をGoogleスプレッドシートに連携させる

data.csv
タイムスタンプ,name,place,緯度,経度,date,time,data,memo
2024/05/15 03:03:22,田中,大阪城,34.6872571,135.5258546,2024/05/09,20:50:00,35.43,	
2024/04/19 20:13:36,佐藤,姫路城,34.8394341,134.6913859,2024/09/11,21:15:00,19.17,

Googleスプレッドシートのコード

スプレッドシートのApps scriptに下記を記入
WEBアプリとしてデプロイすると、そのURLがAPIの働きをする。その際、公開範囲を「全員」にしておくとWEBからもアクセスが可能(一方セキュリティ観点からはリスクがある)。

spreadsheet_to_geojson.gs
function getData(sheetName) {
  //sheet名注意
  var sheet = SpreadsheetApp.getActive().getSheetByName("sheet1");
  //.slice(1)をつけることで、タイトル行を無視
  var rows = sheet.getDataRange().getValues().slice(1);

  //下記でjsonに含めたい項目を設定
  return rows.map(function(row) {
    var obj = {};
    obj.type = "Feature"
    obj.properties = {};
    obj.properties.timestamp = row[0];
    obj.properties.name = row[1];
    obj.properties.place = row[2];
    obj.properties.経度 = row[3];
    obj.properties.緯度 = row[4];
    //DateTime型は扱いにくい場合があるので、指定の形に整形
    obj.properties.date = Utilities.formatDate(row[5], 'JST', "yyyy/MM/dd");
    
    //””の時にエラーが起きるので、場合わけ処理
    if (row[6] == "") {
      //何も実行しない
    }else{
      obj.properties.time = Utilities.formatDate(row[6], 'JST', "HH:mm");
    }
    
    obj.properties.data = row[7];
    obj.properties.times = row[8];
    obj.properties.memo = row[9];

    obj.geometry = {};
    obj.geometry.type = "Point";
    obj.geometry.coordinates = [];
    obj.geometry.coordinates.push(row[4],row[3]);
    return obj;
  });
}

function doGet(e) {
  var data = getData('Sheet1');
  var geojson = {};
  geojson.type = "FeatureCollection";
  geojson.features = data;

  var callback = e.parameter.callback;
  
  if (callback) {
    return ContentService
    .createTextOutput(callback+'('+JSON.stringify(geojson, null, 2)+')')
    .setMimeType(ContentService.MimeType.JAVASCRIPT);
  } else {
     return ContentService
    .createTextOutput(JSON.stringify(geojson, null, 2))
    .setMimeType(ContentService.MimeType.JSON);
  }
}

地図に表示

MapLibreを使って、geojsonを地図表示した。
その際、各地点の数値に従い色分けと数値ラベルつけた。

map.html
<!DOCTYPE html>
<html lang="ja">

<head>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<script src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script>
	<link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet" />
	<title>マップ</title>
</head>

<body style="margin: 0;">
	<div id="map" style="height: 100vh;"></div>
	<script type="module">

		const map = new maplibregl.Map({
			container: 'map', // div要素のid
			zoom: 8, // 初期表示のズーム
			center: [134.860814, 35.127726], // 初期表示の中心
			minZoom: 5, // 最小ズーム
			maxZoom: 18, // 最大ズーム
			pitch: 0,
			maxPitch: 85,
			bearing: 0,
			hash: true,
			localIdeographFontFamily: ['sans-serif'], // 日本語を表示するための設定
			style: {
				version: 8,
				glyphs: "https://glyphs.geolonia.com/{fontstack}/{range}.pbf",// 日本語を表示するための設定
				sources: {
					// 背景地図ソース(Open Street Map)
					osm: {
						type: 'raster',
						tiles: [
							'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
							'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png',
						],
						tileSize: 256,
						attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
					},
				},
				layers: [
					// 背景地図レイヤー
					{
						id: 'osm-layer',
						source: 'osm',
						type: 'raster',
						minZoom: 0,
						maxZoom: 18,
					}
				]
			}
		});

		map.on('load', function () {
			// geojsonを表示する
			map.addSource('result', {
				type: 'geojson',
				data: 'Apps scriptのURLを入力',
			});

			map.addLayer({
				id: 'result',
				type: 'circle',
				source: 'result',
				layout: {
				},
				paint: {
					//dataの数値によってポイントの色を変える
					'circle-color': [
						"interpolate",
						["linear"],
						["get", "data"],
						0, //dataの値が0の時
						"#ffffff", //この色になる
						10,
						"#ff52ff",
						20,
						"#ff0004",
						30,
						"#ff9d00",
						40,
						"#fffb00",
						50,
						"#00ff40",
						60,
						"#00810b",
						70,
						"#007bff",
						80,
						"#0800ff",
						90,
						"#16285d",
						100,
						"#000000",
					],
					'circle-radius': 7 //ポイントの大きさ
				},
			});

			map.addLayer({
				id: 'result_label',
				type: 'symbol',
				source: 'result',
				minzoom: 8,
				//dataをポイントの下にラベル表示
				layout: {
					"text-field": ["get", "data"],
					'text-font': [
						'Noto Sans Regular',
					],
					'text-offset': [0, 1.25],
					'text-anchor': 'top',
					"text-size": [
						"interpolate",
						["linear"],
						["zoom"],
						10, //zoomレベル10の時
						10, //フォントサイズ8
						14, //zoomレベル14の時
						14 //フォントサイズ14
					],
				},
				paint: {
					"text-halo-width": 1,
					"text-halo-color": "#fff"
				},

			});

			// NavigationControl
			let nc = new maplibregl.NavigationControl();
			map.addControl(nc, 'top-left');

			// スケール表示
			map.addControl(new maplibregl.ScaleControl({
				maxWidth: 200,
				unit: 'metric'
			}));


			// 地物クリック時にポップアップを表示する
			map.on('click', "result", function (e) {
				var coordinates = e.features[0].geometry.coordinates.slice();
				var place = e.features[0].properties.place;
				var date = e.features[0].properties.date;
				var time = e.features[0].properties.time;
				var data = e.features[0].properties.data;
				var memo = e.features[0].properties.memo;

				while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
					coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
				}
				// ポップアップを表示する
				new maplibregl.Popup({
					offset: 10, // ポップアップの位置
					closeButton: false, // 閉じるボタンの表示
				})
					.setLngLat(coordinates)
					//ポップアップに表示する内容
					.setHTML("場所 : " + place + "<br>DATA : " + data + "<br>日時 : " + date + " (" + time + ")<br>備考 : " + memo)
					.addTo(map);
			});
		})
	</script>
</body>
</html>

参考

下記を大いにパク参考にさせていただきました。本当にありがとうございます。

  • GAS関係

  • MapLibre GL 関係 #いつもお世話になってます。

  • 聖書、全人類買うべき本

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0