Then update the Code.gs file with:

function doGet() {  const html = HtmlService.createHtmlOutputFromFile(\\'Index\\')    .setTitle(\\'Map with Draggable Points\\')    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);  return html;}

Save, and then click Deploy, and publish as a web app. Then open the link for the new deployment and you should see Leaflet.js displaying a map on New York.

\\\"Building

Ok, that\\'s the regular map example using Leaflet. Now on to the CRS.Simple map type, which allows supplying a background image.

Update the HTML with this example from the Leaflet Tutorials.

  CRS Simple Example - Leaflet          

Here we are supplying an image of 1000 x 1000 pixels, then setting the center marker at 500, 500.

Click Save, then Deploy>Test Deployments, to see the new map type. You should now have a map with a background image and a marker plotted in the center.

\\\"Building

Initializing a Map with Data from Google Sheets

Next, we\\'ll use data from the sheet to populate a set of markers on the map.

First, add a function to the Code.gs file to get the marker locations:

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}

Here I\\'m returning the pins as JSON so they\\'re easier to work with in the HTML in the next section.

Now add a function to the HTML to loop over this JSON and create the map pins after the map has loaded.

// 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(`${pin.title}`).openPopup();        marker.on(\\'dragend\\', function(e) {          const latLng = e.target.getLatLng();          console.log(`Marker ${pin.title} moved to: ${latLng.lat}, ${latLng.lng}`);        });      });    }

Save, and then open the test deployment. You should now have markers generated from your sheet data!

\\\"Building

Each pin has a popup with the title from that row. The pins are draggable at this point, but we still need a function to save the new position.

Saving Marker Position When Dragged

To save the new position, we need two functions: one in the HTML to capture the event on the client side, and one to save the new position on the server side, in the Code.gs file.

Update the HTML with:

    function addMarkers(mapPinData) {      mapPinData.forEach(pin => {        const { id, title, x, y } = pin;        const marker = L.marker([x, y], {          draggable: true        }).addTo(map);        marker.bindPopup(`${title}`).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 });    }

And then add a function to the Code.gs file to save the location:

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;    }  }}

Save, and refresh the test deployment. You should now see the sheet update when a marker is dragged!

\\\"Building

Adding New Points

We can now move the existing points, but what about adding new ones? Again, we\\'ll need two functions, one in the HTML, and one in the Code.gs file.

First, add a function to the HTML to open a prompt when the user clicks an empty spot on the map, and pass the value to a server function.

    // 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(`${title}`).openPopup();      marker.on(\\'dragend\\', function(e) {        const latLng = e.target.getLatLng();        saveMarkerPosition({ id, title, lat: latLng.lat, lng: latLng.lng });      });    }

Then add the function to the Code.gs to save the new row.

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;}

Save once more and refresh the test deployment. Now when you click an empty spot, you can enter a title and save a new marker!

\\\"Building

Deleting A Marker

Lastly, we should add a way to delete markers, giving us a full CRUD app in map view.

Update the add marker function to give the popup a delete button:

      const popupContent = `${title}
`; marker.bindPopup(popupContent).openPopup();

And then add a function for deleting from the client side:

// 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);    }  }

Then add the matching function to the Code.gs file:

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;    }  }}

What\\'s Next?

There\\'s a ton more you could do from here, like adding other data points to each marker, dynamic background images, or other click and drag interactions. You could even make a game! Got an idea for a use case? Drop a comment below!

","image":"http://www.luping.net/uploads/20241011/1728615609670894b9b23dd.png","datePublished":"2024-11-07T01:20:46+08:00","dateModified":"2024-11-07T01:20:46+08:00","author":{"@type":"Person","name":"luping.net","url":"https://www.luping.net/articlelist/0_1.html"}}
”工欲善其事,必先利其器。“—孔子《论语.录灵公》

使用 Google Apps 脚本和 Leaflet.js 构建交互式 XY 图像图

发布于2024-11-07
浏览:551

Google Maps has a ton of features for plotting points on a map, but what if you want to plot points on an image? These XY Image Plot maps are commonly used for floor maps, job site inspections, and even games.

In this guide, I'll show you how to create an interactive map with draggable points using Leaflet.js and Google Apps Script. We'll cover everything from setting up the map to integrating data from Google Sheets, and deploying it as a web app.

This guide will cover:

  • Setting up Leaflet.js in a Google Apps Script HTML Service

  • Displaying Markers using data from Google Sheets

  • Updating Sheets row when a Marker is moved

  • Creating new Markers from the map and saving to Sheets

  • Deleting a marker from the web app

Setting up Leaflet.js in a Google Apps Script HTML Service

Leaflet.js is one of the most popular open-source mapping libraries. It's light-weight, easy to use, and had great documentation. They support a ton of different map types, including "CRS.Simple", or Coordinate Reference System, which allows you to supply a background image.

Google Sheets Set Up

Start out by creating a sheet named map_pin with the following structure:

id title x y
1 test1 10 30
2 test2 50 80

Then open Apps Script from the Extensions menu.

Creating HTML File

First, we'll start with the basic example from the Leaflet docs, just to get the library working. You can see the full example in their quick start guide, here.

Add a new HTML File named Index, and set the content to:



  Quick Start - Leaflet

Then update the Code.gs file with:

function doGet() {
  const html = HtmlService.createHtmlOutputFromFile('Index')
    .setTitle('Map with Draggable Points')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
  return html;
}

Save, and then click Deploy, and publish as a web app. Then open the link for the new deployment and you should see Leaflet.js displaying a map on New York.

Building an Interactive XY Image Plot with Google Apps Script and Leaflet.js

Ok, that's the regular map example using Leaflet. Now on to the CRS.Simple map type, which allows supplying a background image.

Update the HTML with this example from the Leaflet Tutorials.



  CRS Simple Example - Leaflet

Here we are supplying an image of 1000 x 1000 pixels, then setting the center marker at 500, 500.

Click Save, then Deploy>Test Deployments, to see the new map type. You should now have a map with a background image and a marker plotted in the center.

Building an Interactive XY Image Plot with Google Apps Script and Leaflet.js

Initializing a Map with Data from Google Sheets

Next, we'll use data from the sheet to populate a set of markers on the map.

First, add a function to the Code.gs file to get the marker locations:

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
}

Here I'm returning the pins as JSON so they're easier to work with in the HTML in the next section.

Now add a function to the HTML to loop over this JSON and create the map pins after the map has loaded.

// 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(`${pin.title}`).openPopup();

        marker.on('dragend', function(e) {
          const latLng = e.target.getLatLng();
          console.log(`Marker ${pin.title} moved to: ${latLng.lat}, ${latLng.lng}`);
        });
      });
    }

Save, and then open the test deployment. You should now have markers generated from your sheet data!

Building an Interactive XY Image Plot with Google Apps Script and Leaflet.js

Each pin has a popup with the title from that row. The pins are draggable at this point, but we still need a function to save the new position.

Saving Marker Position When Dragged

To save the new position, we need two functions: one in the HTML to capture the event on the client side, and one to save the new position on the server side, in the Code.gs file.

Update the HTML with:

    function addMarkers(mapPinData) {
      mapPinData.forEach(pin => {
        const { id, title, x, y } = pin;
        const marker = L.marker([x, y], {
          draggable: true
        }).addTo(map);

        marker.bindPopup(`${title}`).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 });
    }

And then add a function to the Code.gs file to save the location:

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 



Save, and refresh the test deployment. You should now see the sheet update when a marker is dragged!

Building an Interactive XY Image Plot with Google Apps Script and Leaflet.js

Adding New Points

We can now move the existing points, but what about adding new ones? Again, we'll need two functions, one in the HTML, and one in the Code.gs file.

First, add a function to the HTML to open a prompt when the user clicks an empty spot on the map, and pass the value to a server function.

    // 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(`${title}`).openPopup();

      marker.on('dragend', function(e) {
        const latLng = e.target.getLatLng();
        saveMarkerPosition({ id, title, lat: latLng.lat, lng: latLng.lng });
      });
    }

Then add the function to the Code.gs to save the new row.

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;
}

Save once more and refresh the test deployment. Now when you click an empty spot, you can enter a title and save a new marker!

Building an Interactive XY Image Plot with Google Apps Script and Leaflet.js

Deleting A Marker

Lastly, we should add a way to delete markers, giving us a full CRUD app in map view.

Update the add marker function to give the popup a delete button:

      const popupContent = `${title}
`; marker.bindPopup(popupContent).openPopup();

And then add a function for deleting from the client side:

// 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);
    }
  }

Then add the matching function to the Code.gs file:

function deleteMarker(id) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName('map_pin');
  const data = sh.getDataRange().getValues();

  for (let i = 1; i 



What's Next?

There's a ton more you could do from here, like adding other data points to each marker, dynamic background images, or other click and drag interactions. You could even make a game! Got an idea for a use case? Drop a comment below!

版本声明 本文转载于:https://dev.to/greenflux/building-an-interactive-xy-image-plot-with-google-apps-script-and-leafletjs-2ooe?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何将图标放置在图像或视频上并在点击时触发下载?
    如何将图标放置在图像或视频上并在点击时触发下载?
    在图像或视频上放置图标问题:您需要在图像或视频上放置图标图像或视频,将其与左下角对齐。单击时,该图标应触发图像的下载提示。解决方案:要实现所需的定位,需要在图像周围创建一个相对容器。然后,将图标的位置设置为绝对位置。这是一个代码示例:.container { position: relative...
    编程 发布于2024-11-07
  • 使用 Jsoup 将 HTML 转换为纯文本时如何保留换行符?
    使用 Jsoup 将 HTML 转换为纯文本时如何保留换行符?
    使用 Jsoup 的 Html 到纯文本转换保留换行符Jsoup 提供了强大的 HTML 操作工具,但其默认从 HTML 到纯文本的转换文本可以合并换行符,将它们呈现为连续文本。要保留这些换行符,请按以下方式使用 Jsoup:用于保留换行符的自定义函数:提供的 Java 代码片段引入了一个自定义函数...
    编程 发布于2024-11-07
  • 如何在 C++ 中创建自定义输入流以从非标准源读取数据?
    如何在 C++ 中创建自定义输入流以从非标准源读取数据?
    在 C 中创建自定义输入流 C 中的自定义输入流提供了从非标准源读取数据的强大机制。虽然直接扩展 istream 类似乎是一个可行的选择,但建议探索其他方法以确保高效可靠的实现。从streambuf派生首选方法在 C 中创建自定义流的方法是从 std::streambuf 基类派生自定义的 Stre...
    编程 发布于2024-11-07
  • SharpAPI Laravel 集成指南
    SharpAPI Laravel 集成指南
    Welcome to the SharpAPI Laravel Integration Guide! This repository provides a comprehensive, step-by-step tutorial on how to integrate SharpAPI into y...
    编程 发布于2024-11-07
  • 如何将 Firebase 与 Laravel 集成
    如何将 Firebase 与 Laravel 集成
    Laravel and Firebase are two powerful tools that can significantly enhance the development of modern web applications. Laravel, a popular PHP framewor...
    编程 发布于2024-11-07
  • Expo with Redux Toolkit、文件系统和 Redux Persist:综合指南
    Expo with Redux Toolkit、文件系统和 Redux Persist:综合指南
    Redux Toolkit 是一个流行的库,它通过提供一组实用程序和约定来简化 Redux 开发。它包括一个减速器和动作创建模式,可以简化编写 Redux 逻辑的过程。将 Redux Persist 与 Redux Toolkit 相结合可以显着提高 React Native 应用程序中状态管理的效...
    编程 发布于2024-11-07
  • 如何处理非嵌套 Lambda 闭包中的变量作用域问题?
    如何处理非嵌套 Lambda 闭包中的变量作用域问题?
    Python Lambda 闭包作用域问题将变量封装在闭包中以将其从函数签名中删除是一种常用于高效代码结构的技术。但是,在非嵌套 lambda 的情况下,闭包保留变量的最终值,从而在尝试基于迭代变量访问特定值时导致问题。考虑提供的代码片段:names = ['a', 'b', 'c'] def te...
    编程 发布于2024-11-07
  • 如何使用现代 CSS 将按钮无缝集成到输入字段中?
    如何使用现代 CSS 将按钮无缝集成到输入字段中?
    如何使用现代 CSS 将按钮集成到输入中问题:创建视觉元素其中按钮无缝集成在输入字段中,允许正常的用户交互、保留文本可见性并保持可访问性和屏幕阅读器兼容性。解决方案:Flexbox 和表单边框最佳方法是使用弹性盒布局以及包含元素(表单)上的边框:定位:设置具有水平行布局的弹性盒,允许输入扩展以填充可...
    编程 发布于2024-11-07
  • 内核开发中的 C++:综合指南
    内核开发中的 C++:综合指南
    介绍 由于直接硬件访问和最小的运行时开销,内核开发传统上是 C 的领域。然而,由于其面向对象的特性,C 在内核编程中找到了自己的位置,这可以带来更干净、更易于维护的代码。本指南将逐步介绍如何使用 C 进行内核开发,重点是设置环境、构建项目以及使用 C 功能编写内核代码,同时牢记内核...
    编程 发布于2024-11-07
  • 在 React 项目中实现 CSS 模块
    在 React 项目中实现 CSS 模块
    React 中的 CSS 模块是一种通过自动生成唯一类名来确定 CSS 范围的方法。这可以防止大型应用程序中的类名冲突并允许模块化样式。以下是如何在 React 项目中使用 CSS 模块: 1. 设置 默认情况下,React 支持 CSS 模块。您只需使用扩展名 .module.cs...
    编程 发布于2024-11-07
  • 有哪些资源可用于实现彗星模式?
    有哪些资源可用于实现彗星模式?
    Comet:服务器推送模式服务器推送是一种在服务器和 Web 客户端之间实现双向通信的技术,已经获得了显着的成果最近的兴趣。 Comet 设计模式作为在 JavaScript 应用程序中实现服务器推送的一种有前途的方法而出现。本问题探讨了 Comet 模式的 jQuery 实现和通用资源的可用性。j...
    编程 发布于2024-11-07
  • 探索心理健康门诊项目的类型
    探索心理健康门诊项目的类型
    门诊心理健康治疗方法是一种不强调在医疗机构过夜的方案。这种疗法主要在医生办公室、医院或诊所提供,在那里人们可以进行定期治疗,以进行高度结构化的定期治疗。 在 COVID-19 大流行期间,全球约有 2.75 亿吸毒者。比前几十年高出近 22%。吸毒成瘾的增加导致全美吸毒过量人数创历史新高。好消息是门...
    编程 发布于2024-11-07
  • 如何在 C++ Builder 中初始化 OpenGL 帧:分步指南
    如何在 C++ Builder 中初始化 OpenGL 帧:分步指南
    如何在 C Builder 中初始化 OpenGL 帧在 C Builder 中的窗体内初始化 OpenGL 帧可能是一项具有挑战性的任务。在尝试调整现有 OpenGL 代码(例如问题中提供的示例)时,您可能会遇到困难。要正确创建和渲染 OpenGL 帧,请按照下列步骤操作:使用 TForm::Ha...
    编程 发布于2024-11-07
  • 利用这些罕见的 HTML 属性增强您的 Web 开发技能
    利用这些罕见的 HTML 属性增强您的 Web 开发技能
    Introduction HTML attributes are most often referred to as the overlooked heroes of web development, playing a crucial role in shaping the st...
    编程 发布于2024-11-07
  • 如何在 Python 中将字符串转换为二进制:ASCII 与 Unicode?
    如何在 Python 中将字符串转换为二进制:ASCII 与 Unicode?
    在Python中将字符串转换为二进制在Python中,您可能会遇到需要将字符串表示为二进制数字序列的情况。这对于多种原因都很有用,例如数据加密或二进制文件操作。使用 bin() 函数将字符串转换为二进制的最简单方法就是使用bin()函数。该函数接受一个字符串作为输入,并将其二进制表示形式返回为字符串...
    编程 发布于2024-11-07

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3