Atlaslog
Atlaslog / Examples / OpenLayers / Equal Earth GeoJSON
OpenLayers · Runnable Example

Equal Earth GeoJSON

Runnable OpenLayers example: Equal Earth GeoJSON. 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 OpenLayers examples
/demos/openlayers-equal-earth-geojson.htmlLIVE
single HTML file · CDN dependencies onlyinterface may contain Chinese labels

Source

demos/openlayers-equal-earth-geojson.html
<!--
  示例:Equal Earth 投影 — OpenLayers equal-earth-geojson
  核心逻辑:用解析公式自建投影,GeoJSON 以 4326 读入并转入该投影,底图随视口重投影
  依赖:OpenLayers 10.3.1 · 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>Equal Earth 投影</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@10.3.1/ol.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}
  #map{flex:1 1 auto;min-height:0;background:#eef0f3}
</style>
</head>
<body>
<div class="bar">
  <span>投影 EPSG:8857 · Equal Earth</span>
  <span id="info">—</span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/ol@10.3.1/dist/ol.js"></script>
<script>
(function () {
  'use strict';

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

  /* Equal Earth 公式常数,R 取球面半径 */
  var A1 = 1.340264, A2 = -0.081106, A3 = 0.000893, A4 = 0.003796;
  var M = Math.sqrt(3) / 2;
  var R = 6371007.181;
  var RAD = Math.PI / 180;
  var DEG = 180 / Math.PI;

  function forward(coord) {
    var lon = coord[0] * RAD;
    var lat = coord[1] * RAD;
    var theta = Math.asin(M * Math.sin(lat));
    var t2 = theta * theta, t3 = t2 * theta, t4 = t3 * theta;
    var t5 = t4 * theta, t6 = t5 * theta, t7 = t6 * theta;
    return [
      R * Math.cos(theta) * (A1 + 3 * A2 * t2 + 7 * A3 * t4 + 9 * A4 * t6) * lon,
      R * (A1 * theta + A2 * t3 + A3 * t5 + A4 * t7)
    ];
  }

  function inverse(coord) {
    var x = coord[0] / R;
    var y = coord[1] / R;
    var theta = y / A1;
    for (var i = 0; i < 12; i++) {
      var t2 = theta * theta, t3 = t2 * theta, t4 = t2 * t2, t6 = t4 * t2;
      var f = A1 * theta + A2 * t3 + A3 * t4 * theta + A4 * t6 * theta - y;
      var fp = A1 + 3 * A2 * t2 + 5 * A3 * t4 + 7 * A4 * t6;
      theta -= f / fp;
    }
    var s2 = theta * theta, s4 = s2 * s2, s6 = s4 * s2;
    var lon = x / (Math.cos(theta) * (A1 + 3 * A2 * s2 + 7 * A3 * s4 + 9 * A4 * s6));
    var lat = Math.asin(Math.sin(theta) / M);
    return [lon * DEG, lat * DEG];
  }

  var equalEarth = new ol.proj.Projection({
    code: 'EPSG:8857',
    units: 'm',
    extent: [-26830000, -8387000, 26830000, 8387000],
    global: true
  });
  ol.proj.addProjection(equalEarth);
  ol.proj.addCoordinateTransforms('EPSG:4326', equalEarth, forward, inverse);

  /* 粗略的大陆轮廓,够看出投影的形变特征 */
  var geojson = {
    type: 'FeatureCollection',
    features: [
      {
        type: 'Feature',
        properties: { name: '欧亚大陆' },
        geometry: { type: 'Polygon', coordinates: [[
          [-10, 36], [0, 43], [12, 45], [28, 41], [40, 41], [50, 46],
          [60, 44], [75, 40], [88, 30], [100, 22], [110, 20], [120, 24],
          [122, 38], [130, 43], [140, 50], [150, 60], [170, 66], [180, 68],
          [180, 72], [140, 75], [100, 78], [60, 72], [30, 70], [10, 60],
          [-5, 50], [-10, 36]
        ]] }
      },
      {
        type: 'Feature',
        properties: { name: '非洲' },
        geometry: { type: 'Polygon', coordinates: [[
          [-17, 15], [-5, 5], [8, 4], [10, -2], [13, -12], [20, -20],
          [26, -33], [32, -30], [40, -15], [43, 0], [38, 12], [33, 22],
          [25, 32], [10, 34], [0, 30], [-10, 25], [-17, 15]
        ]] }
      },
      {
        type: 'Feature',
        properties: { name: '美洲' },
        geometry: { type: 'Polygon', coordinates: [[
          [-168, 65], [-140, 70], [-110, 72], [-85, 70], [-60, 55],
          [-55, 45], [-75, 30], [-85, 20], [-100, 18], [-115, 30],
          [-125, 42], [-140, 58], [-168, 65]
        ], [
          [-80, 8], [-60, 10], [-35, -5], [-38, -22], [-55, -35],
          [-70, -45], [-75, -20], [-80, 8]
        ]] }
      },
      {
        type: 'Feature',
        properties: { name: '澳大利亚' },
        geometry: { type: 'Polygon', coordinates: [[
          [114, -22], [122, -18], [135, -15], [145, -18], [153, -28],
          [150, -38], [140, -38], [130, -32], [115, -34], [114, -22]
        ]] }
      }
    ]
  };

  var vectorSource = new ol.source.Vector({
    features: new ol.format.GeoJSON().readFeatures(geojson, {
      dataProjection: 'EPSG:4326',
      featureProjection: 'EPSG:8857'
    })
  });

  var vectorLayer = new ol.layer.Vector({
    source: vectorSource,
    style: function (feature) {
      return new ol.style.Style({
        fill: new ol.style.Fill({ color: 'rgba(31,95,208,0.35)' }),
        stroke: new ol.style.Stroke({ color: '#1f5fd0', width: 2 }),
        text: new ol.style.Text({
          text: feature.get('name'),
          font: '500 13px sans-serif',
          fill: new ol.style.Fill({ color: '#16181d' }),
          stroke: new ol.style.Stroke({ color: '#ffffff', width: 3 })
        })
      });
    }
  });

  var map = new ol.Map({
    target: 'map',
    layers: [
      /* OSM 是 3857 瓦片,视口为 8857,渲染层负责重投影 */
      new ol.layer.Tile({ source: new ol.source.XYZ({url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attributions: 'Tiles © Esri'}) }),
      vectorLayer
    ],
    view: new ol.View({
      projection: 'EPSG:8857',
      center: [0, 0],
      zoom: 1,
      minZoom: 0,
      maxZoom: 10
    }),
    controls: ol.control.defaults.defaults({ attribution: false })
  });

  map.on('moveend', function () {
    var v = map.getView();
    var r = v.getResolution();
    document.getElementById('info').textContent =
      '分辨率 ' + Math.round(r / 1000) + ' km/px';
  });
})();
</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 OpenLayers examples

OpenLayers · EXAMPLEEs2015 Custom Element OpenLayers · EXAMPLEEs2015 Custom Element A11y OpenLayers · EXAMPLEExport Map