Google 地图有大量在地图上绘制点的功能,但是如果您想在图像上绘制点怎么办?这些 XY 图像绘制地图通常用于楼层地图、工地检查,甚至游戏。
在本指南中,我将向您展示如何使用 Leaflet.js 和 Google Apps Script 创建带有可拖动点的交互式地图。我们将介绍从设置地图到集成 Google 表格中的数据以及将其部署为 Web 应用的所有内容。
本指南将涵盖:
在 Google Apps Script HTML 服务中设置 Leaflet.js
使用 Google 表格中的数据显示标记
移动标记时更新表格行
从地图创建新标记并保存到表格
从 Web 应用程序中删除标记
在 Google Apps Script HTML 服务中设置 Leaflet.js
Leaflet.js是最流行的开源地图库之一。它轻量、易用,并且文档丰富。它们支持大量不同的地图类型,包括“ CRS.Simple ”或坐标参考系统,允许您提供背景图像。
Google 表格设置
冷库厂家 www.cqzlsb.com
首先创建一张map_pin
具有以下结构的工作表:
ID | 标题 | 十 | 是 |
1 | 测试1 | 10 | 三十 |
2 | 测试2 | 50 | 80 |
然后从扩展菜单打开 Apps 脚本。
创建 HTML 文件
首先,我们将从 Leaflet 文档中的基本示例开始,以使该库正常运行。您可以在他们的快速入门指南中看到完整的示例,点击此处。
添加一个新的 HTML 文件,命名为 Index,并将内容设置为:
复制
复制
<!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 显示纽约的地图。
好的,这是使用 Leaflet 的常规地图示例。现在介绍 CRS.Simple 地图类型,它允许提供背景图像。
使用 Leaflet 教程中的示例更新 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
。
点击“保存”,然后点击“部署”>“测试部署”,查看新的地图类型。现在您应该有一张带有背景图像且在中心绘制有标记的地图。
使用 Google 表格中的数据初始化地图
接下来,我们将使用工作表中的数据来填充地图上的一组标记。
首先,向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 中使用它们。
现在向 HTML 添加一个函数来循环此 JSON,并在地图加载后创建地图图钉。
复制
复制
// 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}`); }); }); }
保存,然后打开测试部署。现在您应该已经从工作表数据生成了标记!
每个图钉都有一个弹出窗口,其中包含该行的标题。此时图钉是可拖动的,但我们仍需要一个函数来保存新位置。
拖动时保存标记位置
为了保存新位置,我们需要两个函数:一个在 HTML 中,用于捕获客户端的事件;一个在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; } } }
保存并刷新测试部署。现在,当标记被拖动时,您应该会看到工作表更新!
添加新点
我们现在可以移动现有的点,但如何添加新的点呢?同样,我们需要两个函数,一个在 HTML 中,一个在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; } } }
下一步是什么?
您还可以在这里做很多事情,例如向每个标记添加其他数据点、动态背景图像或其他点击和拖动交互。您甚至可以制作游戏!有用例的想法吗?在下面发表评论!
10