ホームページ > 記事 > ウェブフロントエンド > Google Apps Script と Leaflet.js を使用したインタラクティブな XY 画像プロットの構築
Google マップには地図上に点をプロットするための機能がたくさんありますが、画像上に点をプロットしたい場合はどうすればよいでしょうか?これらの XY 画像プロット マップは、フロア マップ、現場検査、さらにはゲームにもよく使用されます。
このガイドでは、Leaflet.js と Google Apps Script を使用して、ドラッグ可能なポイントを含むインタラクティブなマップを作成する方法を説明します。地図の設定から Google スプレッドシートのデータの統合、ウェブ アプリとしての展開まで、すべてを説明します。
このガイドの内容は次のとおりです:
Google Apps Script HTML サービスでの Leaflet.js のセットアップ
Google スプレッドシートのデータを使用してマーカーを表示する
マーカーが移動されたときにシート行を更新する
地図から新しいマーカーを作成し、スプレッドシートに保存します
Web アプリからマーカーを削除する
Leaflet.js は、最も人気のあるオープンソース マッピング ライブラリの 1 つです。軽量で使いやすく、ドキュメントも充実しています。これらは、背景画像を提供できる「CRS.Simple」または座標参照システムを含む、さまざまなマップ タイプをサポートしています。
まず、次の構造を持つ、map_pin という名前のシートを作成します。
id | title | x | y |
---|---|---|---|
1 | test1 | 10 | 30 |
2 | test2 | 50 | 80 |
次に、[拡張機能] メニューから Apps Script を開きます。
まず、ライブラリを動作させるために、Leaflet ドキュメントの基本的な例から始めます。完全な例は、こちらのクイック スタート ガイドでご覧いただけます。
Index という名前の新しい HTML ファイルを追加し、コンテンツを次のように設定します。
<!DOCTYPE html> <html> <head> <title>Quick Start - Leaflet</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" /> <style> #map { height: 400px; } </style> </head> <body> <div id="map"></div> <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script> <script> var map = L.map('map').setView([40.73, -73.99], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(map); var marker = L.marker([40.73, -73.99]).addTo(map) .bindPopup('Test Popup Message') .openPopup(); </script> </body> </html>
次に、Code.gs ファイルを次のように更新します。
function doGet() { const html = HtmlService.createHtmlOutputFromFile('Index') .setTitle('Map with Draggable Points') .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL); return html; }
保存し、[デプロイ] をクリックして、Web アプリとして公開します。次に、新しいデプロイメントのリンクを開くと、Leaflet.js がニューヨークの地図を表示しているのが表示されます。
OK、これは Leaflet を使用した通常の地図の例です。次に、背景画像を提供できる CRS.Simple マップ タイプに移ります。
リーフレット チュートリアルのこの例を使用して HTML を更新します。
<!DOCTYPE html> <html> <head> <title>CRS Simple Example - Leaflet</title> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" /> <style> #map { height: 400px; width: 600px; } body { margin: 0; padding: 0; } </style> </head> <body> <div id="map"></div> <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script> <script> // Set up the map with a simple CRS (no geographic projection) var map = L.map('map', { crs: L.CRS.Simple, minZoom: -1, maxZoom: 4 }); // Define the dimensions of the image var bounds = [[0, 0], [1000, 1000]]; var image = L.imageOverlay('https://leafletjs.com/examples/crs-simple/uqm_map_full.png', bounds).addTo(map); // Set the initial view of the map to show the whole image map.fitBounds(bounds); // Optional: Add a marker or other elements to the map var marker = L.marker([500, 500]).addTo(map) .bindPopup('Center of the image') .openPopup(); </script> </body> </html>
ここでは 1000 x 1000 ピクセルの画像を指定し、中心マーカーを 500、500 に設定します。
保存をクリックし、次に展開>展開のテストをクリックして、新しいマップ タイプを確認します。これで、背景画像と中央にマーカーがプロットされた地図が完成したはずです。
次に、シートのデータを使用して、マップ上に一連のマーカーを設定します。
まず、マーカーの位置を取得する関数を Code.gs ファイルに追加します。
function getPinData(){ const ss = SpreadsheetApp.getActiveSpreadsheet(); const sh = ss.getSheetByName('map_pin'); const data = sh.getDataRange().getValues(); const json = arrayToJSON(data); //Logger.log(json); return json } function arrayToJSON(data=getPinData()){ const headers = data[0]; const rows = data.slice(1); let jsonData = []; for(row of rows){ const obj = {}; headers.forEach((h,i)=>obj[h] = row[i]); jsonData.push(obj) } //Logger.log(jsonData) return jsonData }
ここではピンを JSON として返しているので、次のセクションの HTML での作業が簡単になります。
次に、この JSON をループする関数を HTML に追加し、マップが読み込まれた後にマップ ピンを作成します。
// Add map pins from sheet data google.script.run.withSuccessHandler(addMarkers).getPinData(); function addMarkers(mapPinData) { mapPinData.forEach(pin => { const marker = L.marker([pin.x, pin.y], { draggable: true }).addTo(map); marker.bindPopup(`<b>${pin.title}`).openPopup(); marker.on('dragend', function(e) { const latLng = e.target.getLatLng(); console.log(`Marker ${pin.title} moved to: ${latLng.lat}, ${latLng.lng}`); }); }); }
保存して、テスト展開を開きます。これで、シート データからマーカーが生成されたはずです!
各ピンには、その行のタイトルを含むポップアップがあります。この時点でピンはドラッグ可能ですが、新しい位置を保存する関数がまだ必要です。
新しい位置を保存するには、2 つの関数が必要です。1 つはクライアント側でイベントをキャプチャする HTML 内の関数で、もう 1 つはサーバー側の Code.gs ファイルに新しい位置を保存する関数です。
次のコマンドを使用して HTML を更新します。
function addMarkers(mapPinData) { mapPinData.forEach(pin => { const { id, title, x, y } = pin; const marker = L.marker([x, y], { draggable: true }).addTo(map); marker.bindPopup(`<b>${title}</b>`).openPopup(); marker.on('dragend', function(e) { const latLng = e.target.getLatLng(); console.log(`Marker ${title} moved to: ${latLng.lat}, ${latLng.lng}`); saveMarkerPosition({ id, title, lat: latLng.lat, lng: latLng.lng }); }); }); } function saveMarkerPosition({ id, title, lat, lng }) { google.script.run.saveMarkerPosition({ id, title, lat, lng }); }
次に、関数を Code.gs ファイルに追加して、場所を保存します。
function saveMarkerPosition({ id, lat, lng }) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sh = ss.getSheetByName('map_pin'); const data = sh.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][0] === id) { // ID column (index 0) sh.getRange(i + 1, 3).setValue(lat); // latitude column sh.getRange(i + 1, 4).setValue(lng); // longitude column break; } } }
テスト展開を保存し、更新します。マーカーをドラッグするとシートが更新されるのが確認できるはずです!
既存のポイントを移動できるようになりましたが、新しいポイントを追加する場合はどうすればよいでしょうか?繰り返しますが、2 つの関数が必要になります。1 つは HTML に、もう 1 つは Code.gs ファイルにあります。
まず、ユーザーが地図上の空いている場所をクリックしたときにプロンプトを開く関数を HTML に追加し、その値をサーバー関数に渡します。
// Function to add a new pin map.on('click', function(e) { const latLng = e.latlng; const title = prompt('Enter a title for the new pin:'); if (title) { google.script.run.withSuccessHandler(function(id) { addNewMarker({ id, title, lat: latLng.lat, lng: latLng.lng }); }).addNewPin({ title, lat: latLng.lat, lng: latLng.lng }); } }); function addNewMarker({ id, title, lat, lng }) { const marker = L.marker([lat, lng], { draggable: true }).addTo(map); marker.bindPopup(`<b>${title}</b>`).openPopup(); marker.on('dragend', function(e) { const latLng = e.target.getLatLng(); saveMarkerPosition({ id, title, lat: latLng.lat, lng: latLng.lng }); }); }
次に、関数を Code.gs に追加して新しい行を保存します。
function addNewPin({ title, lat, lng }) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sh = ss.getSheetByName('map_pin'); // Check if there are any rows present, if not initialize ID const lastRow = sh.getLastRow(); let newId = 1; if (lastRow > 0) { const lastId = sh.getRange(lastRow, 1).getValue(); newId = lastId + 1; } sh.appendRow([newId, title, lat, lng]); return newId; }
もう一度保存して、テスト展開を更新します。空の場所をクリックすると、タイトルを入力して新しいマーカーを保存できるようになりました!
最後に、マーカーを削除する方法を追加して、マップ ビューで完全な CRUD アプリを提供する必要があります。
マーカーの追加関数を更新して、ポップアップに削除ボタンを追加します:
const popupContent = `<b>${title}</b><br><button onclick="deleteMarker(${id})">Delete Marker</button>`; marker.bindPopup(popupContent).openPopup();
次に、クライアント側から削除するための関数を追加します。
// Function to delete a marker function deleteMarker(id) { const confirmed = confirm('Are you sure you want to delete this marker?'); if (confirmed) { google.script.run.withSuccessHandler(() => { // Refresh the markers after deletion google.script.run.withSuccessHandler(addMarkers).getPinData(); }).deleteMarker(id); } }
次に、一致する関数を Code.gs ファイルに追加します。
function deleteMarker(id) { const ss = SpreadsheetApp.getActiveSpreadsheet(); const sh = ss.getSheetByName('map_pin'); const data = sh.getDataRange().getValues(); for (let i = 1; i < data.length; i++) { if (data[i][0] === id) { // ID column (index 0) sh.deleteRow(i + 1); // Delete the row break; } } }
各マーカーへの他のデータ ポイントの追加、動的な背景画像、その他のクリック アンド ドラッグ インタラクションなど、ここからできることはたくさんあります。ゲームも作れるかも!使用例についてのアイデアはありますか?以下にコメントを書き込んでください!
以上がGoogle Apps Script と Leaflet.js を使用したインタラクティブな XY 画像プロットの構築の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。