Atlaslog
Atlaslog / Examples / Turf.js / Nearest Point Line
Turf.js · Runnable Example

Nearest Point Line

Runnable Turf.js example: Nearest Point Line. 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 Turf.js examples
/demos/turf-nearest-point-line.htmlLIVE
single HTML file · CDN dependencies onlyinterface may contain Chinese labels

Source

demos/turf-nearest-point-line.html
<!--
  示例:点到线最近点
  核心逻辑:nearestPointOnLine 求线上投影点,配 pointToLineDistance 算垂距
  依赖:Turf.js 7 · jsDelivr CDN
-->
<!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>
<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;color:#16181d;background:#fff}
  .bar{flex:0 0 auto;display:flex;gap:12px;align-items:center;padding:8px 12px;
       background:#f6f7f9;border-bottom:1px solid #e3e6ea;font-size:12px;color:#58606e;flex-wrap:wrap}
  .bar button{font:inherit;font-size:12px;padding:4px 10px;border-radius:4px;cursor:pointer;
       border:1px solid #ccd1d9;background:#fff;color:#16181d}
  .bar button:hover{background:#eef0f3}
  .bar input[type=range]{width:150px}
  .bar b{color:#1f5fd0;font-weight:600}
  .hint{color:#8a929f}
  svg{flex:1 1 auto;min-height:0;width:100%;display:block;background:#fbfcfd}
  .out{flex:0 0 auto;display:flex;gap:20px;flex-wrap:wrap;padding:8px 12px;
       border-top:1px solid #e3e6ea;
       font-family:ui-monospace,Consolas,monospace;font-size:11.5px;color:#3a4250}
  .out b{font-weight:600;color:#1f5fd0}
</style>
</head>
<body>
<div class="bar"><span>点击图上任意位置设置测点</span><button id="reset">重置</button></div>
<svg id="stage" viewBox="0 0 640 320" preserveAspectRatio="xMidYMid meet"></svg>
<div class="out">
  <span>垂距 <b id="dist">—</b></span>
  <span>沿线里程 <b id="along">—</b></span>
  <span>所属段 <b id="idx">—</b></span>
  <span>测点 <span id="p">—</span></span>
</div>
<script src="https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js"></script>
<script>
(function () {
  'use strict';

  if (typeof turf === 'undefined') {
    document.body.innerHTML =
      '<div style="padding:24px;color:#b42318">Turf.js 加载失败,请检查能否访问 cdn.jsdelivr.net</div>';
    return;
  }

  function makeProjection(BOX, W, H, PAD) {
    var kx = Math.cos(((BOX[1] + BOX[3]) / 2) * Math.PI / 180);
    var x0 = BOX[0] * kx, x1 = BOX[2] * kx;
    var y0 = BOX[1], y1 = BOX[3];
    var s = Math.min((W - PAD * 2) / (x1 - x0), (H - PAD * 2) / (y1 - y0));
    var ox = (W - (x1 - x0) * s) / 2;
    var oy = (H - (y1 - y0) * s) / 2;
    var to = function (c) {
      return [(c[0] * kx - x0) * s + ox, (y1 - c[1]) * s + oy];
    };
    to.inverse = function (px, py) {
      return [((px - ox) / s + x0) / kx, y1 - (py - oy) / s];
    };
    return to;
  }

  function linePath(pr, coords) {
    var d = '';
    for (var i = 0; i < coords.length; i++) {
      var p = pr(coords[i]);
      d += (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1);
    }
    return d;
  }

  function ringPath(pr, ring) { return linePath(pr, ring) + 'Z'; }

  function polyPath(pr, rings) {
    var d = '';
    for (var i = 0; i < rings.length; i++) d += ringPath(pr, rings[i]);
    return d;
  }
  var BOX = [116.28, 39.82, 116.52, 39.96];
  var W = 640, H = 320;
  var pr = makeProjection(BOX, W, H, 24);
  var stage = document.getElementById('stage');

  var line = turf.lineString([
    [116.29, 39.86], [116.35, 39.90], [116.41, 39.88],
    [116.47, 39.93], [116.51, 39.89]
  ]);

  var clicked = [116.40, 39.94];

  function draw() {
    var pt = turf.point(clicked);
    var near = turf.nearestPointOnLine(line, pt);
    var meters = turf.pointToLineDistance(pt, line, { units: 'meters' });

    var a = pr(clicked);
    var b = pr(near.geometry.coordinates);

    var parts = ['<rect x="0" y="0" width="640" height="320" fill="none" stroke="#e3e6ea"/>'];
    parts.push('<path d="' + linePath(pr, line.geometry.coordinates) +
               '" fill="none" stroke="#1f5fd0" stroke-width="3"/>');
    parts.push('<line x1="' + a[0].toFixed(1) + '" y1="' + a[1].toFixed(1) +
               '" x2="' + b[0].toFixed(1) + '" y2="' + b[1].toFixed(1) +
               '" stroke="#c2410c" stroke-width="1.6" stroke-dasharray="5 4"/>');
    parts.push('<circle cx="' + a[0].toFixed(1) + '" cy="' + a[1].toFixed(1) +
               '" r="5" fill="#c2410c"/>');
    parts.push('<circle cx="' + b[0].toFixed(1) + '" cy="' + b[1].toFixed(1) +
               '" r="4" fill="#fff" stroke="#1f5fd0" stroke-width="2"/>');
    parts.push('<text x="' + ((a[0] + b[0]) / 2).toFixed(1) + '" y="' +
               (((a[1] + b[1]) / 2) - 8).toFixed(1) +
               '" font-size="11" fill="#c2410c" text-anchor="middle">' +
               meters.toFixed(0) + ' m</text>');

    stage.innerHTML = parts.join('');

    document.getElementById('dist').textContent = meters.toFixed(0) + ' m';
    document.getElementById('along').textContent = near.properties.dist.toFixed(2) + ' km';
    document.getElementById('idx').textContent = near.properties.index;
    document.getElementById('p').textContent =
      clicked[0].toFixed(4) + ', ' + clicked[1].toFixed(4);
  }

  stage.addEventListener('click', function (e) {
    var r = stage.getBoundingClientRect();
    var k = Math.min(r.width / W, r.height / H);
    var ox = (r.width - W * k) / 2;
    var oy = (r.height - H * k) / 2;
    var px = (e.clientX - r.left - ox) / k;
    var py = (e.clientY - r.top - oy) / k;
    var c = pr.inverse(px, py);
    clicked = [
      Math.min(BOX[2], Math.max(BOX[0], c[0])),
      Math.min(BOX[3], Math.max(BOX[1], c[1]))
    ];
    draw();
  });

  document.getElementById('reset').addEventListener('click', function () {
    clicked = [116.40, 39.94];
    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 Turf.js examples

Turf.js · EXAMPLEPoint in Polygon Turf.js · EXAMPLEPoint in Polygon Batch Turf.js · EXAMPLEPoint Line Distance