Atlaslog
Atlaslog / Examples / Leaflet / Graticule
Leaflet · Runnable Example

Graticule

Runnable Leaflet example: Graticule. Open the live demo, read the complete source, and copy the file into your project. No build step or API key.

Open standalone → Chinese notes All Leaflet examples
/demos/leaflet-graticule.htmlLIVE
single HTML file · CDN dependencies onlyinterface may contain Chinese labels

Source

demos/leaflet-graticule.html
<!--
  示例:经纬网格
  要点:按缩放级别选择网格步长、按视野范围生成经纬线、边缘标注 N/E、moveend 重绘
  依赖:Leaflet 1.9.4 · jsDelivr CDN + Esri 瓦片
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>经纬网格</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css">
<style>
  html,body{height:100%;margin:0}
  body{display:flex;flex-direction:column;overflow:hidden;
       font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
  .bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
       display:flex;gap:12px;align-items:center;font-size:12px;color:#58606e}
  .bar .spacer{flex:1 1 auto}
  .bar b{color:#16181d;font-variant-numeric:tabular-nums}
  #map{flex:1 1 auto;min-height:0}
  .glab{font-size:10px;line-height:14px;color:#0e7490;background:rgba(255,255,255,.88);
        padding:0 3px;border-radius:2px;white-space:nowrap;border:1px solid rgba(14,116,144,.25)}
</style>
</head>
<body>
<div class="bar">
  <span>网格步长 <b id="step">—</b></span>
  <span class="spacer"></span>
  <span>拖动或缩放后自动重算</span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
(function () {
  'use strict';

  var map = L.map('map', {
    center: [39.907, 116.391],
    zoom: 12,
    zoomControl: true
  });

  L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
    maxZoom: 19,
    attribution: 'Tiles © Esri'
  }).addTo(map);

  /* 网格与标注放在同一组,便于整体清除 */
  var grid = L.layerGroup().addTo(map);
  var stepEl = document.getElementById('step');

  function stepForZoom(z) {
    if (z <= 3) return 30;
    if (z <= 5) return 10;
    if (z <= 7) return 5;
    if (z <= 9) return 1;
    if (z <= 11) return 0.2;
    if (z <= 13) return 0.05;
    return 0.01;
  }

  function fmtLat(v, step) {
    var digits = step < 0.1 ? 2 : step < 1 ? 1 : 0;
    return Math.abs(v).toFixed(digits) + '°' + (v >= 0 ? 'N' : 'S');
  }

  function fmtLng(v, step) {
    var digits = step < 0.1 ? 2 : step < 1 ? 1 : 0;
    return Math.abs(v).toFixed(digits) + '°' + (v >= 0 ? 'E' : 'W');
  }

  function label(latlng, text, anchor) {
    return L.marker(latlng, {
      interactive: false,
      icon: L.divIcon({
        className: '',
        html: '<span class="glab">' + text + '</span>',
        iconSize: [1, 1],
        iconAnchor: anchor
      })
    });
  }

  function draw() {
    grid.clearLayers();

    var bounds = map.getBounds();
    var step = stepForZoom(map.getZoom());
    stepEl.textContent = step + '°';

    var west = bounds.getWest();
    var east = bounds.getEast();
    var south = bounds.getSouth();
    var north = bounds.getNorth();

    var latStart = Math.ceil(south / step) * step;
    var lngStart = Math.ceil(west / step) * step;

    var lineStyle = {
      color: '#0e7490',
      weight: 1,
      opacity: 0.35,
      dashArray: '4 4',
      interactive: false
    };

    var lat, lng, count = 0;

    for (lat = latStart; lat <= north && count < 60; lat += step, count++) {
      var la = Number(lat.toFixed(6));
      grid.addLayer(L.polyline([[la, west], [la, east]], lineStyle));
      grid.addLayer(label(
        L.latLng(la, west + (east - west) * 0.015),
        fmtLat(la, step),
        [0, 8]
      ));
    }

    count = 0;
    for (lng = lngStart; lng <= east && count < 60; lng += step, count++) {
      var ln = Number(lng.toFixed(6));
      grid.addLayer(L.polyline([[south, ln], [north, ln]], lineStyle));
      grid.addLayer(label(
        L.latLng(north - (north - south) * 0.03, ln),
        fmtLng(ln, step),
        [0, 8]
      ));
    }
  }

  /* move 期间每帧触发,合并到 moveend 与 zoomend 即可 */
  map.on('moveend', draw);
  map.on('zoomend', draw);

  draw();
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.

More Leaflet examples

Leaflet · EXAMPLEHeatmap Leaflet · EXAMPLEImage Overlay Leaflet · EXAMPLEInertia