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
瀏覽:630

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如有侵犯,請聯絡study_golang@163 .com刪除
最新教學 更多>
  • 哪種方法更有效地用於點 - 填點檢測:射線跟踪或matplotlib \的路徑contains_points?
    哪種方法更有效地用於點 - 填點檢測:射線跟踪或matplotlib \的路徑contains_points?
    在Python 射線tracing方法 matplotlib路徑對象表示多邊形。它檢查給定點是否位於定義路徑內。 This function is often faster than the ray tracing approach, as seen in the code snippet pr...
    程式設計 發佈於2025-02-19
  • 如何在JavaScript對像中動態設置鍵?
    如何在JavaScript對像中動態設置鍵?
    如何為JavaScript對像變量創建動態鍵,嘗試為JavaScript對象創建動態鍵,使用此Syntax jsObj['key' i] = 'example' 1;將不起作用。正確的方法採用方括號:他們維持一個長度屬性,該屬性反映了數字屬性(索引)和一個數字屬性的數量。標準對像沒有模仿這...
    程式設計 發佈於2025-02-19
  • 如何可靠地檢查MySQL表中的列存在?
    如何可靠地檢查MySQL表中的列存在?
    在mySQL中確定列中的列存在,驗證表中的列存在與與之相比有點困惑其他數據庫系統。常用的方法:如果存在(從信息_schema.columns select * * where table_name ='prefix_topic'和column_name =&...
    程式設計 發佈於2025-02-19
  • 如何為PostgreSQL中的每個唯一標識符有效地檢索最後一行?
    如何為PostgreSQL中的每個唯一標識符有效地檢索最後一行?
    [2最後一行與數據集中的每個不同標識符關聯。考慮以下數據: 1 2014-02-01 kjkj 1 2014-03-11 ajskj 3 2014-02-01 sfdg 3 2014-06-12 fdsa 為了檢索數據集中每個唯一ID的最後一行信息,您可以在操作員上使用Postgres的有效效...
    程式設計 發佈於2025-02-19
  • 如何使用char_length()在mySQL中按字符串長度對數據進行排序?
    如何使用char_length()在mySQL中按字符串長度對數據進行排序?
    [2使用內置的char_length()函數。 :返回字符串中的字符數,考慮多BYTE字符encoding(例如UTF-8)。 ] :返回字符串佔用的字節數,該字符的數量可能無法準確反映字符計數多字節編碼。 [&& && && && && && &&華從指定的表格中的所有行,並根據指定列的字符長度...
    程式設計 發佈於2025-02-19
  • 如何檢查對像是否具有Python中的特定屬性?
    如何檢查對像是否具有Python中的特定屬性?
    方法來確定對象屬性存在尋求一種方法來驗證對像中特定屬性的存在。考慮以下示例,其中嘗試訪問不確定屬性會引起錯誤: >>> a = someClass() >>> A.property Trackback(最近的最新電話): 文件“ ”,第1行, AttributeError:SomeClass實...
    程式設計 發佈於2025-02-19
  • 在映射到MySQL枚舉列時,如何確保冬眠保留值?
    在映射到MySQL枚舉列時,如何確保冬眠保留值?
    在hibernate中保存枚舉值:故障排除錯誤的列type ,他們各自的映射至關重要。在Java中使用枚舉類型時,至關重要的是,建立冬眠的方式如何映射到基礎數據庫。 在您的情況下,您已將MySQL列定義為枚舉,並在Java中創建了相應的枚舉代碼。但是,您遇到以下錯誤:“ MyApp中的錯誤列類型...
    程式設計 發佈於2025-02-19
  • 如何使用替換指令在GO MOD中解析模塊路徑差異?
    如何使用替換指令在GO MOD中解析模塊路徑差異?
    克服go mod中的模塊路徑差異 github.com/coreos/etcd/integration imports :解析GO.mod:模塊將其路徑聲明為: go.etcd.io/bbolt [&&&&&&&&&&&&&&&&&&&&&&&&&&&& github.com/coreos/b...
    程式設計 發佈於2025-02-19
  • 如何使用PHP從XML文件中有效地檢索屬性值?
    如何使用PHP從XML文件中有效地檢索屬性值?
    從php 您的目標可能是檢索“ varnum”屬性值,其中提取數據的傳統方法可能會使您留下PHP陷入困境。 使用simplexmlelement :: attributes()函數提供了簡單的解決方案。此函數可訪問對XML元素作為關聯數組的屬性: - > attributes()為$ att...
    程式設計 發佈於2025-02-19
  • Java是否允許多種返回類型:仔細研究通用方法?
    Java是否允許多種返回類型:仔細研究通用方法?
    在java中的多個返回類型:一個誤解介紹,其中foo是自定義類。該方法聲明似乎擁有兩種返回類型:列表和E。但是,情況確實如此嗎? 通用方法:拆開神秘 [方法僅具有單一的返回類型。相反,它採用機制,如鑽石符號“ ”。 分解方法簽名: :本節定義了一個通用類型參數,E。它表示該方法接受擴展FOO類的...
    程式設計 發佈於2025-02-19
  • 大批
    大批
    [2 數組是對象,因此它們在JS中也具有方法。 切片(開始):在新數組中提取部分數組,而無需突變原始數組。 令ARR = ['a','b','c','d','e']; // USECASE:提取直到索引作...
    程式設計 發佈於2025-02-19
  • 在沒有密碼提示的情況下,如何在Ubuntu上安裝MySQL?
    在沒有密碼提示的情況下,如何在Ubuntu上安裝MySQL?
    在ubuntu 使用debconf-set-selections 在安裝過程中避免密碼提示mysql root用戶。這需要以下步驟: sudo debconf-set-selections
    程式設計 發佈於2025-02-19
  • 如何以不同的頻率控制Android設備振動?
    如何以不同的頻率控制Android設備振動?
    控制使用頻率變化的Android設備振動是否想為您的Android應用程序添加觸覺元素?了解如何觸發設備的振動器至關重要。您可以做到這一點:生成基本振動以生成簡單的振動,使用振動器對象:這將導致設備在指定的持續時間內振動。 許可要求通過上述技術,您可以創建在您的Android應用程序中自定義振動,以...
    程式設計 發佈於2025-02-19
  • 如何克服PHP的功能重新定義限制?
    如何克服PHP的功能重新定義限制?
    克服PHP的函數重新定義限制在PHP中,多次定義一個相同名稱的函數是一個no-no。嘗試這樣做,如提供的代碼段所示,將導致可怕的“不能重新列出”錯誤。 //錯誤:“ cance redeclare foo()” 但是,PHP工具腰帶中有一個隱藏的寶石:runkit擴展。它使您能夠靈活地重新定...
    程式設計 發佈於2025-02-19

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3